Runtime Animation with Silverlight

As I progress to our game development with Silverlight, I was asked how to animate a UIElement in silverlight 3 from code without using XAML or the Objects and Timeline.

Here is the simplest implementation that I have.

Storyboard myStoryBoard = new Storyboard();
DoubleAnimation myDoubleAnimation = new DoubleAnimation();
Storyboard.SetTarget(myDoubleAnimation, myTargetObject);
Storyboard.SetTargetProperty(myDoubleAnimation, new PropertyPath("Opacity"));
myStoryBoard.Completed += (object sender, EventArgs e) =>
{
	//--Animation Completed
};
myDoubleAnimation.From = 1;
myDoubleAnimation.To = 0;
myDoubleAnimation.Duration = new TimeSpan(0, 0, 0, 0, 5000);
myStoryBoard.Children.Add(myDoubleAnimation);
myStoryBoard.Begin();

Lets Inspect the code.

First we created a new instance of StoryBoard class where our animated object resides.

Storyboard myStoryBoard = new Storyboard();

Then we create a double animation for our UIElements’ property with double or numeric value.

DoubleAnimation myDoubleAnimation = new DoubleAnimation();

After creating an instance of double animation, we then set our target object.

Storyboard.SetTarget(myDoubleAnimation, myTargetObject);

We also need to set our target property through this line.

Storyboard.SetTargetProperty(myDoubleAnimation, new PropertyPath("Opacity"));

If we have other things to do after our animation is completed, we have to set the
storyboards Completed event.

myStoryBoard.Completed += (object sender, EventArgs e) =>
{
	//--Animation Completed
};

We need to provide a value also for our animations starting point which is achieved through this line.

myDoubleAnimation.From = 1;

And our destination point or value which is 0. In this case we are actually making a fade out effect.

myDoubleAnimation.To = 0;

Then of course the duration of our animation in milliseconds (1000ms=1s)

myDoubleAnimation.Duration = new TimeSpan(0, 0, 0, 0, 5000);

We are done with setting up our double animation. Its time we add it to our storyboard.

myStoryBoard.Children.Add(myDoubleAnimation);

And finally to begin our animation. We call a descriptive method called Begin();

myStoryBoard.Begin();

That’s it. With this simple implementation we can achieve animations or effects like fade-in fade-out. Or move an object from point a to point b.
My next post would be easing, for smooth animations.

Storyboard vStoryboard = new Storyboard();
DoubleAnimation vDoubleAnimation = new DoubleAnimation();
Storyboard.SetTarget(vDoubleAnimation, cSlotsGrid.Children[i]);
Storyboard.SetTargetProperty(vDoubleAnimation, new PropertyPath(“Opacity”));
if (i == cSlotsGrid.Children.Count – 1)
{
vStoryboard.Completed += (object sender, EventArgs e) =>
{
SlotsShowSecondaryCommon();
};
}vDoubleAnimation.From = 1;
vDoubleAnimation.To = 0;
vDoubleAnimation.Duration = new TimeSpan(0, 0, 0, 0, Convert.ToInt32((double)GetValue(HidePauseMillisecondsProperty) / 2));
vStoryboard.Children.Add(vDoubleAnimation);
vStoryboard.Begin();

Advertisements

The Advantage of Being a OO Developer – The jQuery ASP.NET to WPF experience

Many of us developers have experienced the pain of accepting whatever our clients want (CHANGES). Some of us accept it negatively and some of us do accept it with passion (and i am one of those developers). Last Wednesday, I was told that the online version of my application due to network problems had shown some unusual behavior. The performance was affected, fetching of data became slow including the saving process of those information was heavily affected. Due to the current situation, my boss proposed a solution that would solve the problem. And the answer was an offline version of the online system. The objective was, whenever the system is not connected to the network, the application must access an offline copy of the system’s database, and whenever an internet connection is detected by the application, 1.) the local copy must be updated and 2.) the user must be informed whether he has some un-submitted records which needs to be saved to the server. With the objective on-hand, I told myself, thank God, I can just consume the methods and functions exposed by my BO Layer.

Before everything else, let me show you the UI  transformation which took 1 day, from ASP.NET with jQuery to WPF. Of course it took another 8 hours for making the application work. A programmer can simply create a new object that inherits many of its features from existing objects. This makes object-oriented programs easier to modify.

The jQuery,ASP.NET and C# version

cyber_jquery

The WPF with C# version.

cyber_wpf

How did I convert the application? – Take advantage of the OO principles.

OO by definition

One of the principal advantages of object-oriented programming techniques over procedural programming techniques is that they enable programmers to create modules that do not need to be changed when a new type of object is added.

With the definition above, I did change the User Interface but never changed the code that made the application do its purpose.

The Design

Architecture

The Code.

Note: The code shown below is an example of binding records, in the case of ASP.NET and jQuery we used JSON, for WPF, we used ADO.NET’s DataTable.

ASP.NET and jQuery


$("#btnRetrieveList").click(function() {
 HideDrps();
 document.getElementById("drpBrandForSellout").style.visibility="hidden";
 document.getElementById("drpCategoriesForSellout").style.visibility="hidden";
 ModalPopupsIndicator2("Fetching Data...");
 totalCount=0;
 $("#divListOfModels").html("");
 scroll(0,0);
 var brandid=$("#drpBrandForSellout").attr("value");
 var categoryid=$("#drpCategoriesForSellout").attr("value");
 $.getJSON('ModelsForSelloutHandler.ashx?brandId='+brandid+'&categoryId='+categoryid,function(mymodellist) {
 var chkId=0;
 $.each(mymodellist,function() {
 $("#divListOfModels").append("<div id='divModel"+chkId+"' style='float: left; position: relative; width: 960px;'><div style='float: left; position: relative; width: 50px; text-align: center;'>"+
 "<input type='checkbox' onclick='ChangeBack("+chkId+");' name='chkModel"+chkId+"' id='chkModel"+chkId+"'/></div><div style='float: left; position: relative; width: 150px; text-align: left;'>"+
 this['cnmodelname']+"<input type='hidden' id='hdnModelName"+chkId+"' value='"+this['cnmodelname']+"'></div><div style='float: left; position: relative; width: 80px; text-align: left;'>"+
 this['cnbrandname']+"</div><div style='float: left; position: relative; width: 90px; text-align: left;'>"+
 this['cncategoryname']+"</div><div style='float: left; position: relative; width: 90px; text-align: left;'>"+
 "<input type='text' name='txtSeloutQty"+chkId+"' id='txtSeloutQty"+chkId+"' style='text-align:right;width:80px; border-style:solid; border-color:silver; border-width:1px; padding:2px; font-family:verdana; font-size:11px;'/></div><div style='float: left; position: relative; width: 90px; text-align: left;'>"+
 "<input type='text' name='txtInvQty"+chkId+"' id='txtInvQty"+chkId+"' style='text-align:right;width:80px; border-style:solid; border-color:silver; border-width:1px; padding:2px; font-family:verdana; font-size:11px;'/></div><div style='float: left; position: relative; width: 90px; text-align: left;'>"+
 "<input type='text' name='txtSKUDisp"+chkId+"' id='txtSKUDisp"+chkId+"' style='text-align:right;width:80px; border-style:solid; border-color:silver; border-width:1px; padding:2px; font-family:verdana; font-size:11px;'/></div><div style='float: left; position: relative; width: 90px; text-align: left;'>"+
 "<input type='text' name='txtSKUBU"+chkId+"' id='txtSKUBU"+chkId+"' style='text-align:right;width:80px; border-style:solid; border-color:silver; border-width:1px; padding:2px; font-family:verdana; font-size:11px;'/></div><div style='float: left; position: relative; width: 90px; text-align: left;'><input name='txtSelloutRemarks"+chkId+"' id='txtSelloutRemarks"+chkId+"' type='text' style='width:180px; border-style:solid; border-color:silver; border-width:1px; padding:2px; font-family:verdana; font-size:11px;'/></div></div><div style='float: left; position: relative; width: 960px; height:1px; overflow:hidden; background-color:silver;'></div>");
 chkId=chkId+1;
 totalCount=totalCount+1;
 });
 CloseIndicator();
 });
 });

— The IHttpHandler for ASP.NET


public class ModelsForSelloutHandler : IHttpHandler {

 public void ProcessRequest(HttpContext context) {
 //drpCategories.DataSource = m_boSelloutEntry.GetCategories(1, Convert.ToInt32(drpBrands.SelectedValue));
 //drpCategories.DataBind();

 int brandId = Convert.ToInt32(context.Request.QueryString["brandId"]);
 int categoryId = Convert.ToInt32(context.Request.QueryString["categoryId"]);

 Sellouts boSelloutEntry = new Sellouts();
 DataTable dtModelsForCart = boSelloutEntry.GetModelsForCart(brandId, categoryId);

 System.Text.StringBuilder sbModelsForCart = new System.Text.StringBuilder();
 sbModelsForCart.Append("[");

 for (int i = 0; i < dtModelsForCart.Rows.Count; i++) {
 sbModelsForCart.Append("{");
 sbModelsForCart.Append("\"cnmodelid\":\"" + dtModelsForCart.Rows[i]["cnmodelid"].ToString() + "\",");
 sbModelsForCart.Append("\"cnmodelname\":\"" + dtModelsForCart.Rows[i]["cnmodelname"].ToString() + "\",");
 sbModelsForCart.Append("\"cncategoryname\":\"" + dtModelsForCart.Rows[i]["cncategoryname"].ToString() + "\",");
 sbModelsForCart.Append("\"cnbrandname\":\"" + dtModelsForCart.Rows[i]["cnbrandname"].ToString() + "\",");
 sbModelsForCart.Append("\"cncategoryid\":\"" + dtModelsForCart.Rows[i]["cncategoryid"].ToString() + "\",");
 sbModelsForCart.Append("\"cnbrandid\":\"" + dtModelsForCart.Rows[i]["cnbrandid"].ToString() + "\"");
 sbModelsForCart.Append("},");
 }
 string sr = sbModelsForCart.ToString().Remove(sbModelsForCart.Length - 1, 1) + "]";

 context.Response.ContentType = "application/json";
 context.Response.ContentEncoding = System.Text.Encoding.UTF8;
 context.Response.Write(sr.ToString());
 context.Response.End();
 }

 public bool IsReusable {
 get {
 return false;
 }
 }
 }

WPF


void btnGetModels_Click(object sender, RoutedEventArgs e) {
 dgInfraModelList.DataSource = boSellouts.GetModelsForCart(Convert.ToInt32(drpBrandForSellout.SelectedValue), Convert.ToInt32(drpCategoryForSellout.SelectedValue)).DefaultView;
 }

If you’ll notice, we have lots of lines from our ASP.NET version that’s because we used jQuery and created an on the fly Tabular list of records. In the case of our WPF version we did not convert our datatable to JSON. Either way, we still called  GetModelsForCart() function (from our BOLayer) both for our ASP.NET and WPF version.

Conclusion

We can do more by reusable objects. Please watch out for my next post, because I am going to start posting about animations on silverlight and WPF.

jQuery Autosuggest Textbox

Want something other than dropdownlist? How about providing a fully responsive autosuggest textbox that quickly responses to every letter or character that you type? Yes it is possible with ASP.NET, with the help of my favourite language (C#) and jQuery.

The Process.

1. On page load, fetch all the data that needs to be listed, for example, cities, countries, product categories, products.
2. Load the data in an array in javascript through JSON.
3. In every character that you input in your textbox, match it with every word in your array with regards to the position of the character.
4. Display all the items that matched your criteria in the textbox.

The Architecture.

Web Form                         Generic Handler        Business Object Layer/Helper Class           Data Layer

process

The Logic Behind Each Process.

Web Form


<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
 <title>Untitled Page</title>

 <script src="suggestions.js" type="text/javascript"></script>

 <script src="autosuggest.js" type="text/javascript"></script>

 <script src="jquery.js" type="text/javascript"></script>

 <script language="javascript" type="text/javascript">
 var modelArray = new Array();
 $(window).load(function(){
 $.getJSON("ProductsHandler.ashx", function(result) {
 var pos = 0;
 $.each(result, function() {
 modelArray[pos] = this['cnmodelname'];
 pos = pos + 1;
 });

 });
 });
 $(document).ready(function() {

 $("#txtProducts").focus(function() {
 var oTextbox=new AutoSuggestControl(document.getElementById("txtProducts"),new ProductsSuggestions());
 });
 });

 </script>

 <style>
 div.suggestions
 {
 -moz-box-sizing: border-box;
 box-sizing: border-box;
 border: 1px solid #cccccc;
 position: absolute;
 }
 div.suggestions div
 {
 cursor: default;
 padding: 0px 3px;
 }
 div.suggestions div.current
 {
 background-color: #3366cc;
 color: white;
 }
 </style>
</head>
<body style="margin-top: 100; margin-left: 100;">
 <form id="form1" runat="server">
 <div>
 <input type="text" id="txtProducts" />
 </div>
 </form>
</body>
</html>

Generic Handler


using System;
using System.Data;
using System.Text;
using System.Web;

namespace TestWebApplication {

public class ProductsHandler : IHttpHandler {

public void ProcessRequest(HttpContext context) {

string results = "";

Products p = new Products();
DataTable dt = p.GetCurrentProducts();
StringBuilder sb = new StringBuilder();
sb.Append("[");
foreach (DataRow item in dt.Rows) {
sb.Append("{");
sb.Append("\"cnmodelid\":\""+item["cnmodelid"].ToString()+"\",");
sb.Append("\"cnmodelname\":\"" + item["cnmodelname"].ToString() + "\"");
sb.Append("},");
}
results = sb.ToString();
results = results.Remove(results.Length - 1, 1) + "]";

context.Response.ContentType = "application/json";
context.Response.ContentEncoding = Encoding.UTF8;
context.Response.Write(results);
context.Response.End();
}

public bool IsReusable {
get {
return false;
}
}
}
}

Business Object Layer/Helper Class


using System;
using System.Data;
using System.Data.SqlClient;
using System.Collections.Generic;

namespace TestWebApplication {

public class Product  {
DataLayer dal;
public Product() {
dal = new DataLayer();
}

public DataTable GetCurrentProducts() {
using (SqlCommand sqlCommand = new SqlCommand()) {
sqlCommand.CommandText = "sp_GetProducts";
sqlCommand.CommandType = CommandType.StoredProcedure;

return dal.GetRecords(sqlCommand);
}
}
}
}

Data Layer


using System;
using System.Data;
using System.Data.SqlClient;

namespace TestWebApplication {
public class DataLayer {

public DataTable GetRecords(SqlCommand sqlCommand) {
using (SqlConnection sqlCon = new SqlConnection(@"Data Source=.\SQLEXPRESS;Initial Catalog=OnlineBackupApplication;User Id=constantine;Password=100885101#;")) {
sqlCon.Open();
SqlCommand sqlCmd = sqlCommand;
sqlCmd.Connection = sqlCon;
SqlDataAdapter sqlDa = new SqlDataAdapter(sqlCmd);

DataTable dtLocal = new DataTable();
sqlDa.Fill(dtLocal);
sqlCon.Close();
return dtLocal;
}
}

}
}

Our Records

data

Our Output

data2

Note:

Please download this scripts from this site. http://www.phpguru.org/static/AutoComplete.html
1. suggestions.js
2. autosuggest.js

Changes Made to suggestions.js

1. Added the below functions to get the exact location of our textbox.

</pre>
function getAbsolutePosition(element){
 var ret = new Point();
 for(;
 element && element != document.body;
 ret.translate(element.offsetLeft, element.offsetTop), element = element.offsetParent
 );

 return ret;
}

function Point(x,y){
 this.x = x || 0;
 this.y = y || 0;
 this.toString = function(){
 return '('+this.x+', '+this.y+')';
 };
 this.translate = function(dx, dy){
 this.x += dx || 0;
 this.y += dy || 0;
 };
 this.getX = function(){ return this.x; }
 this.getY = function(){ return this.y; }
 this.equals = function(anotherpoint){
 return anotherpoint.x == this.x && anotherpoint.y == this.y;
 };
}

Changes Made to autosuggest.js

1. Changed getLeft function to the following block of code.


AutoSuggestControl.prototype.getLeft = function () {
var iLeft = getAbsolutePosition(document.getElementById("txtProducts")).x;
return iLeft;
};

2. Changed getTop function to the following block of code.


AutoSuggestControl.prototype.getTop = function () {
var iTop = getAbsolutePosition(document.getElementById("txtProducts")).y;
return iTop;
};

[/sourcecode]