Click here to Skip to main content
Click here to Skip to main content
 

How to perform fading transitions on Textures in XNA 4.0 using Interpolation Timers

By , 7 Jul 2012
Rate this:
Please Sign up or sign in to vote.

Introduction

Having used XNA Game Studio 3.1 for a while then taking a break, I had trouble figuring out to to apply alpha to textures and do transformations. I could not seem to find any decent tutorials out there for this purpose, so I sat down while writing a screensaver in XNA 4.0 and clumped these two classes together.

They are simple and easy to use, well commented and documented so everyone should be able to follow along without any trouble.

Please be gentle as this is my first (and probably only) article/tutorial. 

Assumptions about you:  

I am assuming that you have at least some experience with XNA Game Studio 4.0 and C# and that you are able to:

  • Create a Windows Game Project 
  • Add a texture to your project
  • Add classes to your project (with supplied code)
  • Debug any errors that occur 
  • Understand fairly simple code (no matrices or quaternions or trigonometry here...) 

Using the code 

There are two classes that you need to create in your project: 

  • InterpolationTimer.cs - This is a custom timer class that can be used as a simple timer or a timer that takes in a minimum and maximum value and automatically interpolates between minimum and maximum over the duration
  • FadingTexture.cs - This class utilises 4 InterpolationTimers to automate the transition of a texture after a delay if required, from fully transparent, to opaque, then back to transparent.  
I will give you the code for the two classes first, then explain how to use them. The code is straight forward so I don't feel that I need to go through it with you. If you have any questions though, feel free to ask and I'll try to answer as best as I can.

If you have any trouble copying the code from this page, I have attached a demo project that is working here: Download Demo Project and Source Code 

The InterpolationTimer Class 

#region Timer Finished Event Delegate
internal delegate void TimerFinished();
#endregion
/// <summary>
/// A Timer class that can automatically interpolate between two values over the specified time, or simply run for the specified time. Raises an event when complete.
/// </summary>
internal class InterpolationTimer
{
    #region Private Members
    private TimeSpan _timeLimit;
    private TimeSpan _timeElapsed;
    private bool _isRunning = false;
    private float _deltaPerMillisecond;
    private float _currentValue;
    private float _minValue, _maxValue;
    private bool _interpolate;
    #endregion
    #region Internally-accessible Properties
    /// <summary>
    /// Gets the Time Limit of this timer. To Set the time limit, please use SetTimeLimit().
    /// </summary>
    internal TimeSpan TimeLimit
    {
        get { return _timeLimit; }
    }
    /// <summary>
    /// Gets the current time elapsed of this timer.
    /// </summary>
    internal TimeSpan TimeElapsed
    {
        get { return _timeElapsed; }
    }
    /// <summary>
    /// Returns true if the timer is currently running.
    /// </summary>
    internal bool IsRunning
    {
        get { return _isRunning; }
    }
    /// <summary>
    /// This event will be fired once the timer is complete.
    /// </summary>
    internal event TimerFinished OnTimerFinished;
    /// <summary>
    /// This value is calculated by the class.
    /// It is the delta value of CurrentValue, per millisecond, that the timer uses to interpolate based on gametime.
    /// </summary>
    internal float DeltaPerMillisecond
    {
        get {
            if (_interpolate)
                return _deltaPerMillisecond;
            else
                throw new NullReferenceException("You cannot retrieve DeltaPerMillisecond from a non-interpolated timer!");
        }
    }
    /// <summary>
    /// The current value of interpolation.
    /// </summary>
    internal float CurrentValue
    {
        get {
            if (_interpolate)
                return _currentValue;
            else
                throw new NullReferenceException("You cannot retrieve CurrentValue from a non-interpolated timer!");
        }
    }
    #endregion
    #region Constructors
    /// <summary>
    /// Creates a Timer that interpolates currentValue between minValue and maxValue over timeLimit.
    /// Raises an event when the timer is finished. Very useful for fading textures in/out, or moving along an axis at a steady speed over time.
    /// To interpolate backwards, simply input the higher value as the minValue.
    /// </summary>
    /// <param name="timeLimit">A TimeSpan variable containing the total time that the timer is to run before finishing.</param>
    /// <param name="minValue">The minimum value to interpolate from.</param>
    /// <param name="maxValue">The maximum value to interpolate to.</param>
    internal InterpolationTimer(TimeSpan timeLimit, float minValue, float maxValue)
    {
        _timeLimit = timeLimit;
        _timeElapsed = TimeSpan.Zero;
        // calculate the amount to interpolate by per millisecond of gametime.
        _deltaPerMillisecond = (maxValue - minValue) / (float)TimeLimit.TotalMilliseconds;
        _minValue = minValue;
        _maxValue = maxValue;
        _interpolate = true;
    }
    /// <summary>
    /// Creates a Timer that raises an event once the time limit has been reached.
    /// </summary>
    /// <param name="timeLimit">A TimeSpan variable containing the total time that the timer is to run before finishing.</param>
    internal InterpolationTimer(TimeSpan timeLimit)
    {
        _timeLimit = timeLimit;
        Stop();  // make sure it's not running
        _timeElapsed = TimeSpan.Zero;
        _interpolate = false;
    }
    #endregion
    #region Timer Control Methods
    /// <summary>
    /// Starts the timer.
    /// </summary>
    internal void Start()
    {
        _timeElapsed = TimeSpan.Zero;
        _isRunning = true;
        if (_interpolate)
        {
            // set the current value to the minimum
            _currentValue = _minValue;
        }
    }
    /// <summary>
    /// Stops the timer. Automatically called when the elapsed time reaches the time limit.
    /// </summary>
    internal void Stop()
    {
        _timeElapsed = TimeSpan.Zero;
        _isRunning = false;
    }
    /// <summary>
    /// Update the time limit for an interpolating timer. Will throw an exception if the timer is running.
    /// You must provide minValue and maxValue so the DeltaPerMillisecond can be recalculated based on the new time limit!
    /// </summary>
    /// <param name="newTimeLimit">The new time limit.</param>
    /// <param name="minValue">Minimum value for interpolation.</param>
    /// <param name="maxValue">Maximum value for interpolation.</param>
    internal void SetTimeLimit(TimeSpan newTimeLimit, float minValue, float maxValue)
    {
        if (!_interpolate)
        {
            SetTimeLimit(newTimeLimit);
            return;
        }
        if (IsRunning)
            throw new ApplicationException("You must stop the timer before changing the time limit!");
        // recalculate deltapermillisecond
        _deltaPerMillisecond = (maxValue - minValue) / (float)newTimeLimit.TotalMilliseconds;
        _timeLimit = newTimeLimit;
    }
    /// <summary>
    /// Update the time limit for a non-interpolating timer.
    /// NOTE: Will throw an exception if this IS an interpolating timer or if the timer is running.
    /// </summary>
    /// <param name="newTimeLimit"></param>
    internal void SetTimeLimit(TimeSpan newTimeLimit)
    {
        if (_interpolate)
            throw new NullReferenceException("You must re-specify the minimum and maximum values for an interpolation timer! See - SetTimeLimit(time, minValue, maxValue)");
        if (IsRunning)
            throw new ApplicationException("You must stop the timer before changing the time limit!");
        _timeLimit = newTimeLimit;
    }
    #endregion
    #region Update Method
    /// <summary>
    /// Call this from your game's update method. Or if you are using this class with my FadingTexture class, it will call this automatically for you.
    /// </summary>
    /// <param name="elapsedTime">The gameTime.ElapsedGameTime value (from your Update Method)</param>
    internal void Update(TimeSpan elapsedTime)
    {
        if (IsRunning)
        {
            // add elapsed time
            _timeElapsed += elapsedTime;
            // if we are interpolating between minValue and maxValue, do it here
            if (_interpolate)
                _currentValue += DeltaPerMillisecond * (float)elapsedTime.TotalMilliseconds;
            // check if we have matched or exceeded the time limit, if so, 
            // stop the timer and raise the OnTimerFinished event if it is assigned to
            if (_timeElapsed >= _timeLimit)
            {
                // make sure final value is what it should be (value is always *slightly* lower (like 0.0005 off for me!)
                if (_currentValue != _maxValue)
                    _currentValue = _maxValue;
                // stop the timer
                Stop();
                // trigger the event if it is assigned to
                if (OnTimerFinished != null)
                    OnTimerFinished();
            }
        }
    }
    #endregion
} 

The FadingTexture Class

This class is a DrawableGameComponent, which means it will integrate with your game and your game will actually call its update and draw methods automatically.  

#region Sequence Complete Event Delegate
internal delegate void FadeSequenceComplete();
#endregion
/// <summary>
/// A class that utilises the InterpolationTimer class to fade a texture in and out.
/// </summary>
internal class FadingTexture : DrawableGameComponent
{
    #region Private Members
    /// <summary>
    /// Used only by this class, hence inside the scope of the class.
    /// Keeps track of the current fade state.
    /// </summary>
    enum FadeState { InitialDelay, FadingIn, Opaque, FadingOut, Complete };
    private Texture2D _splashTexture;
    private Game1 _game;
    private Vector2 _position;
    private TimeSpan _fadeInTime, _fadeOutTime, _opaqueTime, _initialDelayTime;
    private FadeState _currentState;
    private float _opacity;
    #endregion
    #region Internally-accessible Properties & Members
    // timers
    internal InterpolationTimer FadeInTimer, OpaqueTimer, FadeOutTimer, InitialDelayTimer;
    /// <summary>
    /// This event will fire once the entire sequence (initial delay + fade in + opaque + fade out) is completed.
    /// If you wish, you can use this event to call Reset() which sets the sequence to fire again at a later time.
    /// </summary>
    internal event FadeSequenceComplete OnFadeSequenceCompleted;
    /// <summary>
    /// Gets the sum duration of the entire sequence.
    /// </summary>
    internal TimeSpan TotalDuration
    {
        get {
            return _fadeInTime + _fadeOutTime + _opaqueTime + _initialDelayTime;
        }
    }
    #endregion
    #region Constructors
    /// <summary>
    /// Creates a FadingTexture sequence WITHOUT an initial delay (eg, fade in will begin immediately).
    /// </summary>
    /// <param name="game">Obvious.</param>
    /// <param name="position">The *top-left* coordinates of the texture.</param>
    /// <param name="texture">The texture to fade in and out.</param>
    /// <param name="fadeInTime">How long you want the fade in transition to last.</param>
    /// <param name="opaqueTime">How long you want the texture to be fully opaque.</param>
    /// <param name="fadeOutTime">How long you want the fade out transition to last.</param>
    internal FadingTexture(Game1 game, Vector2 position, Texture2D texture, TimeSpan fadeInTime, TimeSpan opaqueTime, TimeSpan fadeOutTime)
        : base(game)
    {
        _game = game;
        // add to game components
        if (!_game.Components.Contains(this))
            _game.Components.Add(this);
        // make sure this draws on top of everything else
        DrawOrder = 30000;
        // set members according to constructor params
        _splashTexture = texture;
        _fadeInTime = fadeInTime;
        _opaqueTime = opaqueTime;
        _fadeOutTime = fadeOutTime;
        _initialDelayTime = TimeSpan.Zero;
        _currentState = FadeState.FadingIn;
        _position = position;
        // call initialize, since the game may not call it if it instantiated at the wrong time (I still don't get how that works...)
        Initialize();
    }
    /// <summary>
    /// Creates a FadingTexture sequence WITH an initial delay (eg, fade in will begin *AFTER* the initial delay has passed).
    /// </summary>
    /// <param name="game">Obvious.</param>
    /// <param name="position">The *top-left* coordinates of the texture.</param>
    /// <param name="texture">The texture to fade in and out.</param>
    /// <param name="initialDelayTime">How long you want the class to wait before starting the fade in transition.</param>
    /// <param name="fadeInTime">How long you want the fade in transition to last.</param>
    /// <param name="opaqueTime">How long you want the texture to be fully opaque.</param>
    /// <param name="fadeOutTime">How long you want the fade out transition to last.</param>
    internal FadingTexture(Game1 game, Vector2 position, Texture2D texture, TimeSpan initialDelayTime, TimeSpan fadeInTime, TimeSpan opaqueTime, TimeSpan fadeOutTime)
        : base(game)
    {
        _game = game;
        // add to game components
        if (!_game.Components.Contains(this))
            _game.Components.Add(this);
        // make sure this draws on top of everything else
        DrawOrder = 30000;
        // set members according to constructor params
        _splashTexture = texture;
        _fadeInTime = fadeInTime;
        _opaqueTime = opaqueTime;
        _fadeOutTime = fadeOutTime;
        _initialDelayTime = initialDelayTime;
        _currentState = FadeState.InitialDelay;
        _position = position;
        // call initialize, since the game may not call it if it instantiated at the wrong time (I still don't get how that works...)
        Initialize();
    }
    #endregion
    #region Overridden XNA Methods (Initialize, Update & Draw)
    public override void Initialize()
    {
        base.Initialize();
        // if we have an initial delay, create the initial delay timer.
        if (_currentState == FadeState.InitialDelay)
        {
            InitialDelayTimer = new InterpolationTimer(_initialDelayTime);
            InitialDelayTimer.OnTimerFinished += new TimerFinished(InitialDelayTimer_OnTimerFinished);
        }
        // set up timers and their events
        FadeInTimer = new InterpolationTimer(_fadeInTime, 0.0f, 1.0f);
        FadeInTimer.OnTimerFinished += new TimerFinished(FadeInTimer_OnTimerFinished);
        OpaqueTimer = new InterpolationTimer(_opaqueTime);
        OpaqueTimer.OnTimerFinished += new TimerFinished(OpaqueTimer_OnTimerFinished);
        FadeOutTimer = new InterpolationTimer(_fadeOutTime, 1.0f, 0.0f);
        FadeOutTimer.OnTimerFinished += new TimerFinished(FadeOutTimer_OnTimerFinished);
    }
    public override void Update(GameTime gameTime)
    {
        base.Update(gameTime);
 
        // respond appropriately to the current state
        switch (_currentState)
        {
            case FadeState.InitialDelay:
                if (!InitialDelayTimer.IsRunning)
                    InitialDelayTimer.Start();
                InitialDelayTimer.Update(gameTime.ElapsedGameTime);
                _opacity = 0.0f; // going to be fully transparent at this stage
                break;
            case FadeState.FadingIn:
                if (!FadeInTimer.IsRunning)
                    FadeInTimer.Start();
                FadeInTimer.Update(gameTime.ElapsedGameTime);
                _opacity = FadeInTimer.CurrentValue;
                break;
            case FadeState.Opaque:
                if (!OpaqueTimer.IsRunning)
                    OpaqueTimer.Start();
                OpaqueTimer.Update(gameTime.ElapsedGameTime);
                _opacity = 1.0f;
                break;
            case FadeState.FadingOut:
                if (!FadeOutTimer.IsRunning)
                    FadeOutTimer.Start();
                FadeOutTimer.Update(gameTime.ElapsedGameTime);
                _opacity = FadeOutTimer.CurrentValue;
                break;
        }
        // prevent calls to Draw() unless the texture is visible
        if (_opacity > 0.0f)
            Visible = true;
        else
            Visible = false; 
    }
    public override void Draw(GameTime gameTime)
    {
        base.Draw(gameTime);
        _game.spriteBatch.Begin();
        _game.spriteBatch.Draw(_splashTexture, _position, Color.White * _opacity);
        _game.spriteBatch.End();
    }
    #endregion
    #region Custom Methods
    /// <summary>
    /// Resets the current sequence's InitialDelayTimer to delay and begins anew.
    /// </summary>
    /// <param name="delay">The time to wait before starting the fade sequence again.</param>
    internal void Reset(TimeSpan delay)
    {
        if (_currentState != FadeState.Complete)
            throw new Exception("Please wait until the sequence is complete before calling Reset!");
        _initialDelayTime = delay;
        // make sure InitialDelayTimer is not null (which would be the case if no initial delay was specified in the constructor)
        if (InitialDelayTimer == null)
        {
            InitialDelayTimer = new InterpolationTimer(delay);
            InitialDelayTimer.OnTimerFinished += new TimerFinished(InitialDelayTimer_OnTimerFinished);
        }
        else
            InitialDelayTimer.SetTimeLimit(delay);
        _currentState = FadeState.InitialDelay;
    }
    #endregion
    #region InterpolationTimer Events
    void FadeOutTimer_OnTimerFinished()
    {
        _currentState = FadeState.Complete;
        // fire timer complete event if it is assigned to
        if (OnFadeSequenceCompleted != null)
            OnFadeSequenceCompleted();
    }
    void OpaqueTimer_OnTimerFinished()
    {
        _currentState = FadeState.FadingOut;
    }
    void FadeInTimer_OnTimerFinished()
    {
        _currentState = FadeState.Opaque;
    }
    void InitialDelayTimer_OnTimerFinished()
    {
        _currentState = FadeState.FadingIn;
    }
    #endregion
} 

Using the FadingTexture Class 

You'll need to load a Texture2D in your Game.LoadContent method, and then pass that into the constructor of this class.

If you don't like doing it that way, I'm sure you're smart enough to figure out how to make this class load a texture. 

There are 2 constructors for this class, one that sets up an initial delay before fading in your texture, and one that fades the texture in immediately.

Once you have instantiated the class, it will do everything for you. No other code in your Game.Update or Game.Draw is necessary. 

1). Make the spriteBatch variable in your Game class INTERNAL or PUBLIC so this class can access it: 

public class Game1 : Microsoft.Xna.Framework.Game
{
    GraphicsDeviceManager graphics;
    internal SpriteBatch spriteBatch; // YOU MUST MAKE THIS ACCESSIBLE TO INTERNAL OR PUBLIC SCOPE, OR ADD YOUR OWN SPRITEBATCH TO THE FADING TEXTURE CLASS!
    // ...
}  

2). In order for this class to access your spritebatch, it needs to know the type of your game.

Look at the code in the FadingTexture class and change the following to suit your game. If your game class is just Game1, you can skip this step. 

In FadingTexture.cs:  

// change "Game1" to the name of your game class in each of the following lines:
private Game1 _game; 
internal FadingTexture(Game1 game, Vector2 position, Texture2D texture, TimeSpan fadeInTime, TimeSpan opaqueTime, TimeSpan fadeOutTime)
internal FadingTexture(Game1 game, Vector2 position, Texture2D texture, TimeSpan initialDelayTime, TimeSpan fadeInTime, TimeSpan opaqueTime, TimeSpan fadeOutTime)   

3). Define the class variable in your game, and if you haven't already, a Texture2D containing the texture, and TimeSpans for fadeIn, Opaque, fadeOut and InitialDelay if applicable. 

In Game1.cs: (or whatever your Game class file name is) 

// VARIABLES
FadingTexture _myFadingTexture;
FadingTexture _myFadingTextureDelayed;
Texture2D _myTexture;
Vector2 _texturePosition;
TimeSpan _fadeInTime = TimeSpan.FromSeconds(1.5),
         _fadeOutTime = TimeSpan.FromSeconds(1.5),
         _opaqueTime = TimeSpan.FromSeconds(5);
// if you want an initial delay...
         TimeSpan _delayTime = TimeSpan.FromSeconds(3); // don't worry, we won't make this occur at the same time as the immediate transition, see below 

4). Instantiate the class AFTER YOU HAVE LOADED YOUR CONTENT  

In Game1.cs:  

protected override void LoadContent()
{
    // load your content here
    _myTexture = Content.Load<Texture2D>(@"Textures\fadeTexture");
             
    // ... load the rest of your content if you have any
             
    // set where you want your texture to be on the screen (top-left coordinates, so if we want it to be centered we need
    // to take half of the texture dimensions away from the screen center
    Viewport vp = graphics.GraphicsDevice.Viewport;
    Vector2 textureSize = new Vector2(_myTexture.Width, _myTexture.Height);
    _texturePosition = new Vector2(vp.Width / 2, vp.Height / 2) - textureSize / 2;
    // now instantiate the class
    // for an immediate fade-in:
    _myFadingTexture = new FadingTexture(this, _texturePosition, _myTexture, _fadeInTime, _opaqueTime, _fadeOutTime);
    // or for an initial delay (make sure you've set this variable above or you can call the static timespan methods directly)
    // here is a useful property:
    delayTime += _myFadingTexture.TotalDuration; // this gets the total transition time of _myFadingTexture, then adds _delayTime on to it
    _myFadingTextureDelayed = new FadingTexture(this, _texturePosition, _myTexture, _delayTime, _fadeInTime, _opaqueTime, _fadeOutTime);
} 

Step 5). Run your game and watch the prettiness!! 

If you would like your game to be notified when the transition is completed, simply subscribe to the OnFadeSequenceCompleted event like so:

In Game1.cs or another class that needs to be notified when the transition is complete: 

_myFadingTextureDelayed.OnFadeSequenceCompleted += new FadeSequenceComplete(_myFadingTextureDelayed_OnFadeSequenceCompleted);
// then do whatever you want when the event is triggered:
void _myFadingTextureDelayed_OnFadeSequenceCompleted()
{
    // you can dispose the object...
    _myFadingTextureDelayed.Dispose();
    // or you can reset it with a delay:
    _myFadingTextureDelayed.Reset(TimeSpan.FromMinutes(1)); // will start the fade in exactly 1 minute from when this is called
} 

If you're able to understand the code of the FadingTexture class then you should realise that you do not need to interact with any InterpolationTimer classes. FadingTexture does all of that for you. 

If you just want the Timer aspect of the classes, here is how to use the InterpolationTimer class without the FadingTexture class. 

Using the InterpolationTimer Class without the FadingTexture Class 

A Simple Timer 

1). Create your instance of the class and subscribe to the OnTimerFinished event

// VARIABLE
// define the timer
InterpolationTimer _myTimer;
// INSTANTIATION
// instantiate it in a function somewhere (Game.Initialize is good)
_myTimer = new InterpolationTimer(TimeSpan.FromSeconds(10)); // makes a timer that will fire the completed event 10 seconds after it is started
// subscribe to the completed event
_myTimer.OnTimerFinished += new TimerFinished(_myTimer_OnTimerFinished); // use intellisense to automatically create the method (after you type +=, press TAB), you can rename it later if you wish 

2). Make sure the timer is updated when it is running, or nothing will happen! 

// In your game's UPDATE method:
protected override void Update(GameTime gameTime)
{
    base.Update(gameTime);
    if (_myTimer.IsRunning)
        _myTimer.Update(gameTime.ElapsedGameTime);
} 

3). Start your timer when you see fit 

_myTimer.Start(); 

4). Your event should be triggered when the timer is complete. If nothing happens, check you are updating the timer in your Update method correctly. 

// your intellisense-generated code should look like this:
void _myTimer_OnTimerFinished()
{
    // delete this and the next line, it will stop your game unless you are handling NotImplementedExceptions, but it's a good way to test the timer is working.
    throw new NotImplementedException();
} 

An Interpolating Timer 

The only difference from the above code is the overloaded constructor and how you fetch the interpolation value.

1). Create your instance of the class and subscribe to the OnTimerFinished event 

// VARIABLE
// define the timer
InterpolationTimer _myInterpolationTimer;
// INSTANTIATION
// instantiate it in a function somewhere (Game.Initialize is good)
_myInterpolationTimer = new InterpolationTimer(TimeSpan.FromSeconds(10), 0.0f, 1.0f); // timer will change _myInterpolationTimer.CurrentValue from 0.0f to 1.0f over 10 seconds
// or if you want to interpolate backwards:
_myInterpolationTimer = new InterpolationTimer(TimeSpan.FromSeconds(10), 1.0f, 0.0f); // timer will change _myInterpolationTimer.CurrentValue from 1.0f to 0.0f over 10 seconds
// subscribe to the completed event
_myInterpolationTimer.OnTimerFinished += new TimerFinished(_myInterpolationTimer_OnTimerFinished); // use intellisense to automatically create the method (after you type +=, press TAB), you can rename it later if you wish 

2). Use the code for simple timer above to make sure your timer is updated (rename the variable from _myTimer to _myInterpolationTimer though) 

You'll probably want to use the interpolation value here in your update method, to do that, simply reference _myInterpolationTimer.CurrentValue as follows: 

// In your game's UPDATE method:
protected override void Update(GameTime gameTime)
{
    base.Update(gameTime);
    if (_myInterpolationTimer.IsRunning)
        _myInterpolationTimer.Update(gameTime.ElapsedGameTime);
             
    _textureAlphaLevel = _myInterpolationTimer.CurrentValue;
} 

3). Start the timer the same way as above

4). Your event will be triggered the same way as above 

To draw an object using the alpha value generated by the timer, simply draw it as normal, with a Color of White, multiplied by the alpha value, like follows: 

// in your game's Draw method: 
spriteBatch.Begin();
spriteBatch.Draw(texture, position, Color.White * _textureAlphaLevel);
spriteBatch.End();  

I hope at least a few people find this useful!



 

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

CJ Kent

Australia Australia
I program as a hobby, and work full time at an embroidery shop of all places XD.

Comments and Discussions

 
GeneralMy vote of 5 [modified] PinmemberCDP18027-Jul-12 19:38 
GeneralRe: My vote of 5 PinmemberCJ Kent8-Jul-12 0:53 
GeneralRe: My vote of 5 PinmemberCDP18028-Jul-12 1:57 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web03 | 2.8.140415.2 | Last Updated 7 Jul 2012
Article Copyright 2012 by CJ Kent
Everything else Copyright © CodeProject, 1999-2014
Terms of Use
Layout: fixed | fluid