Friday, March 6, 2009

Using previously indtroduced Tween

In my previous post I introduced a Tween class, which lets you to manipulate object Properties in timely manner.
Let's consider we have a simple button on client area, which has "btnButton" name. The following code will change the button's width from 50 to 300 pixels in 3 seconds (3000 milliseconds).

Tween tmpTween = new Tween();
tmpTween.MaxValue = 300;
tmpTween.MinValue = 50;
tmpTween.Period = 10000;
tmpTween.Target = this.btnTest;
tmpTween.Property = "Height";
tmpTween.Start();

Tween in Silverlight

During my Silverlight development I noticed that I need a feature which present in Adobe Flash - the tween. It's a class which lets you to increase a property for a given object from MinValue to MaxValue in a given time period.
So I have implemented it in C# for Silverlight 2. Here is the code for it.


///ITween.cs - Tween support for Silverlight 2
///The code is written by Artak Mkrtchyan
///Exposed under GNU license at http://mkartak.blogspot.com
///Contact me by: mkartak@gmail.com
public interface ITween
{
void Start();
void Stop();

double Period
{
get;
set;
}

object Target
{
get;
set;
}

string Property
{
get;
set;
}

double MinValue
{
get;
set;
}

double MaxValue
{
get;
set;
}
}


///Tween.cs - Tween support for Silverlight 2
///The code is written by Artak Mkrtchyan
///Exposed under GNU license at http://mkartak.blogspot.com
///Contact me by: mkartak@gmail.com
public class Tween : ITween
{
private object target;
private string property;

private double period;

private double minValue;
private double maxValue;

private bool started = false;
private PropertyInfo targetPropertyInfo;
private double currentValue;

private DispatcherTimer timer;

public Tween()
{

}

#region ITween Members

public void Start()
{
if (!this.started)
{
if (this.Period < 0)
{
throw new ArgumentOutOfRangeException("Period must be more than zero");
}

if (this.Target == null)
{
throw new ArgumentNullException("Target");
}

if (this.Property == null)
{
throw new ArgumentNullException("Property");
}

if (this.Property.Trim() == String.Empty)
{
throw new ArgumentException("Property must have a value");
}

this.started = true;

this.targetPropertyInfo = this.Target.GetType().GetProperty(this.Property);
this.currentValue = this.MinValue;
this.timer = new DispatcherTimer();
this.timer.Tick += new EventHandler(this.OnTimerEvent);
this.timer.Interval = TimeSpan.FromMilliseconds(this.MinPeriod);
this.timer.Start();
}
}

public void Stop()
{
if (this.started)
{
this.started = false;
this.timer.Stop();
}
}

public double Period
{
get
{
return this.period;
}
set
{
this.period = value;
}
}

public object Target
{
get
{
return this.target;
}
set
{
this.target = value;
}
}

public string Property
{
get
{
return this.property;
}
set
{
this.property = value;
}
}

public double MinValue
{
get
{
return this.minValue;
}
set
{
if (this.started)
{
throw new Exception("Unable to change value during progress");
}

if (value >= this.maxValue)
{
throw new ArgumentException("MinValue must be less than the maxValue");
}

this.minValue = value;
}
}

public double MaxValue
{
get
{
return this.maxValue;
}
set
{
if (this.started)
{
throw new Exception("Unable to change value during progress");
}

if (value <= this.minValue)
{
throw new ArgumentException("MaxValue must be more than the minValue");
}

this.maxValue = value;
}
}

#endregion

private double Step
{
get
{
return (this.MaxValue - this.MinValue) / StepCount;
}
}

private double Speed
{
get
{
return (this.MaxValue - this.MinValue) / this.Period;
}
}

private double StepCount
{
get
{
return Math.Ceiling(this.Period / this.MinPeriod);
}
}

private int MinPeriod
{
get
{
return 10;
}
}

private void OnTimerEvent(object sender, EventArgs e)
{
if (this.started)
{
lock (this)
{
this.currentValue = Math.Min(this.currentValue + this.Step, this.MaxValue);

this.targetPropertyInfo.SetValue(this.Target, this.currentValue, null);

if (this.currentValue == this.MaxValue)
{
this.Stop();
}
}
}
}
}