C# Passing Values Between Multiple Projects using Interface

Earlier when I was doing some major update on a project I was working on for a client, I was also thinking how to optimize the code, not just by using MVP pattern and also the way of passing values between projects. Some of my class libraries where using the same type of property and then gets updated in the main form. I noticed though that setting the same value into the multiple objects with one type of property is pretty hard and unmanageable and also producing a lot of lines of codes. So I experimented a little bit and made an proof on concept how to pass values into multiple projects with minimal coding and using Interface

Let's see the sample animation below. What was shown there is we have satellite that moves in the form and there are 3 child forms. Those 3 child forms are the other 3 projects which are PlanetEarth, PlanetMars, and PlanetSaturn project and the big the form is our Main project. You'll notice that the 3 forms are getting updated everytime the satellite make its next move.

What is so interesting in using an Interface is you can share its values to whoever uses it. Like what I did on that simple P.O.S. project. Those 3 forms, PlanetEarth, PlanetMars, and PlanetSaturn is using one interface but they all get the update.

So why Interface instead of making a property for each project?

Because like I said, you can share the values between multiple projects or whoever uses it.

Let's get into the code

In this sample VS solution. We have 5 projects.

GlobalInterface class library project which contains the Interface code which have an NextPosition property signature what we're going to use later on the Main Project

public interface iSatellite
{
	Point NextPosition { get; set; }
}

Then we have 3 projects that has the same code (For demo only) which is using this iSatellite interface. These are the PlanetEarth, PlanetMars, and PlanetSaturn. Let's take PlanetEarth for example

GlobalInterface.iSatellite Satellite;

public Form1()
{
	InitializeComponent();

	timer1.Interval = 100;
	timer1.Tick += new EventHandler(timer1_Tick);
}

void timer1_Tick(object sender, EventArgs e)
{
	// update the location information of the satellite
	label2.Text = string.Format("X: {0}\r\nY:{1}", this.Satellite.NextPosition.X, this.Satellite.NextPosition.Y);
}

public void InitializeSatellite(GlobalInterface.iSatellite satellite)
{
	this.Satellite = satellite;
	this.timer1.Start();
}

As you noticed, we used Timer object to show the next location of the satellite. We could also use Event to shout that the satellite is making a next move.

InitializeSatellite(GlobalInterface.iSatellite satellite) will be called on our Main Project to bind the "this.Satellite" into our Main project.

Next is our Main project that implements iSatellite interface

public partial class MainController : Form, GlobalInterface.iSatellite
{
	TweenLibrary tween;
	Random destXY = new Random(DateTime.Now.Millisecond);
	List<Form> forms;

	public MainController()
	{
		InitializeComponent();
		InitializeControls();
		InitializeControlEvents();
	}

	public void InitializeControls()
	{
		// show our 3 forms
		PlanetEarth.Form1 earth = new PlanetEarth.Form1();
		earth.InitializeSatellite(this);
		earth.Show();

		PlanetMars.Form1 mars = new PlanetMars.Form1();
		mars.InitializeSatellite(this);
		mars.Show();

		PlanetSaturn.Form1 saturn = new PlanetSaturn.Form1();
		saturn.InitializeSatellite(this);
		saturn.Show();

		// then put them on the list to update their location
		forms = new List<Form>()
		{
			earth, mars, saturn
		};

		tween = new TweenLibrary();

		timer1.Interval = Convert.ToInt32(TimeSpan.FromSeconds(3).TotalMilliseconds);
		timer1.Tick += new EventHandler(timer1_Tick);
		timer1.Start();
	}

	public void InitializeControlEvents()
	{
		this.LocationChanged += new EventHandler(Form1_LocationChanged);
	}

	/// <summary>
	/// update child form positions
	/// </summary>
	/// <param name="sender"></param>
	/// <param name="e"></param>
	void Form1_LocationChanged(object sender, EventArgs e)
	{
		int last_y = this.Location.Y;

		foreach (Form frm in forms)
		{
			frm.Location = new Point(this.Location.X + this.Size.Width, last_y);
			last_y = frm.Location.Y + frm.Size.Height;
		}
	}

	void timer1_Tick(object sender, EventArgs e)
	{
		this.NextPosition = new Point()
		{
			X = destXY.Next(this.ClientSize.Width),
			Y = destXY.Next(this.ClientSize.Height)
		};

		tween.startTweenEvent(picSatellite, this.NextPosition.X, this.NextPosition.Y, "easeinoutcubic", 100);
	}

	/// <summary>
	/// Implement GlobalInterface.iSatellite property
	/// </summary>
	public Point NextPosition { get; set; }
}

As you can see just below on that code, we implemented iSatellite interface with NextPosition property with a Point type which we're going to use to update the location of the satellite. In the timer1_Tick event, this gets triggered every 3 seconds and you can see, we update the "NextPosition" X and Y values. The 3 projects that is bound in the interface are automatically updated.

So what is the benefit of the Interface in this type of problem about passing values between multiple visual studio projects?

  • Lesser code
  • We do not need to update the values for each project.
  • Easy to manage
  • More structured.

Comparing the usage of Property vs Interface

Let's say each projects PlanetEarth, PlanetMars, and PlanetSaturn are using NextPosition Property

void timer1_Tick(object sender, EventArgs e)
{
	Point newXY = new Point()
	{
		X = destXY.Next(this.ClientSize.Width),
		Y = destXY.Next(this.ClientSize.Height)
	};
	
	this.earth.NextPosition = newXy;
	this.mars.NextPosition = newXy;
	this.saturn.NextPosition = newXy;
}

But when we use Interface

void timer1_Tick(object sender, EventArgs e)
{
	this.NextPosition = new Point()
	{
		X = destXY.Next(this.ClientSize.Width),
		Y = destXY.Next(this.ClientSize.Height)
	};

	tween.startTweenEvent(picSatellite, this.NextPosition.X, this.NextPosition.Y, "easeinoutcubic", 100);
}

We only update the property we implemented from iSatellite and all 3 projects are automatically updated as well.

Hope you learned about another special way of implementing Interface in your projects, not only in Windows Forms, but in Windows Phone, Windows 8, Silverlight, and WPF projects as well.

Here's the a sample project that you can play with.

Click here to expand the blog post

Determine Development and Production Connection Strings for ASP.NET or Windows Forms Programatically

Often in development and production stage, you have to change your connection strings if you're developing and sending your app/web .config to the production/deployment servers. Some may use <connectionstring> and some use <appSettings> to store their connection strings.

This method only applies for someone who uses <appSettings> section to add their connection strings. If so, please continue reading.

Thank you. So we want to make our life easier and we want to prevent bad things happen when we're deploying our applications. 

The trick here is to add two connection string keys in <appSettings> section

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <appSettings>
    <add key="constr_development" value="connection string for development machine"/>
    <add key="constr_production" value="connection string for production"/>
  </appSettings>
</configuration>

So for example, the first key would be your development connection string and the second would be your production connection string. So the next step is how does your code determine which one to use? Every OS gives their local PC a name to be able to determine how you are when you're in a network. To get the your machine name programmatically, you need to use System.Net.Dns.GetHostName() then setup a condition that matches your machinename like this

public string ConfigurationConnectionString
{
	get
	{
		string hostname = System.Net.Dns.GetHostName();

		if (hostname == "my-computer-name") // change it to your default computer name
		{
			return "constr_development";
		}
		else
		{
			return "constr_production";
		}
	}
}

then in SqlConnection.ConnectionString, do this

SqlConnection sqlconn = new SqlConnection;
sqlconn.ConnectionString = ConfigurationConnectionString;

simple isn't it? This way, we don't have to worry updating the connection string.

Click here to expand the blog post

Gambit Surface Rapahael ROM - HTC Fuze ROM

The first ever ROM I made for Windows Mobile and it's called Gambit Surface ROM. I worked so hard with this and shared to the Raphael family in XDA-Dev. Now it's time to move on with Windows Phone 7 world and it's time to let go of this ROM and hoping somebody could continue.

Head over to this page to download the kitchen

Click here to expand the blog post

Session Aware Web Client

I tried looking for some free session aware web clients but I couldn't find a better one so I made my own instead.

/*
 * Session Aware Web Client Library v1.3
 * ---------------
 * Date: 12/17/2010
 * Author: Jayson Ragasa
 * http://wall.jaysonragasa.net
 * License: Common Development and Distribution License (CDDL)
 */

using System;
using System.Collections;
using System.Collections.Specialized;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Net;
using System.Web;

namespace Nullstring.Modules.WFWebClient
{
    public class SessionAwareWebClientLibrary
    {
        #region vars

        string _referer = string.Empty;

        ArrayList _params;
        CookieContainer cookieko = new CookieContainer();
        HttpWebRequest req = null;
        HttpWebResponse resp = null;
        Uri uri = null;

        #endregion

        #region properties
        public string Referer
        {
            get { return _referer; }
            set { _referer = value; }
        }

        bool _list_cookies = false;
        public bool ListCookiesInOutputWindow
        {
            get { return _list_cookies; }
            set { _list_cookies = value; }
        }

        public CookieContainer Cookie
        {
            get { return cookieko; }
            set { cookieko = value; }
        }
        #endregion

        #region constructor
        public SessionAwareWebClientLibrary()
        {
            _params = new ArrayList();
            cookieko = new CookieContainer();
        }
        #endregion

        #region methods
        public void ClearParameter()
        {
            _params.Clear();
        }

        public void AddParameter(string key, string value)
        {
            _params.Add(string.Format("{0}={1}", WebTools.URLEncodeString(key), WebTools.URLEncodeString(value)));
        }

        void SetupWebRequests(string URL)
        {
            uri = new Uri(URL);
            req = (HttpWebRequest)WebRequest.Create(uri);
			
            req.AllowAutoRedirect = true;
            req.KeepAlive = true;
            req.Accept = "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8";
            req.UserAgent = "Mozilla/5.0 (Windows NT 6.1; rv:8.0) Gecko/20100101 Firefox/8.0";
			
			// handle respnose cookie
            req.CookieContainer = cookieko;
			
			// if we made the first request
            if (resp != null)
            {
				// just reuse it
                req.CookieContainer.Add(resp.Cookies);
            }			
			
			// some optional properties
			//req.MaximumAutomaticRedirections = 20;
            //req.Timeout = 300;
            //req.ImpersonationLevel = System.Security.Principal.TokenImpersonationLevel.Impersonation;
            //req.UseDefaultCredentials = true;
			//req.Referer = this._referer;
			
			// get rid of 100 service bug
            req.ServicePoint.Expect100Continue = false;
        }

        void BuildPostData()
        {
            string Parameters = String.Join("&", (String[])this._params.ToArray(typeof(string)));

            ASCIIEncoding encoding = new ASCIIEncoding();
            byte[] loginDataBytes = encoding.GetBytes(Parameters);

            req.Method = "POST";
            req.ContentType = "application/x-www-form-urlencoded";
            req.ContentLength = loginDataBytes.Length;

            Stream stream = req.GetRequestStream();
            stream.Write(loginDataBytes, 0, loginDataBytes.Length);
            stream.Close();
        }

        StringBuilder GetWebResponse()
        {
            StringBuilder response = new StringBuilder();

            resp = (HttpWebResponse)req.GetResponse();

            Stream resStream = resp.GetResponseStream();

            int bytesReceived = 0;
            string tempString = null;
            int count = 0;
            byte[] buf = new byte[8192];

            do
            {
                count = resStream.Read(buf, 0, buf.Length);

                if (count != 0)
                {
                    bytesReceived += count;
                    tempString = Encoding.UTF8.GetString(buf, 0, count);
                    response.Append(tempString);
                }
            }
            while (count > 0);

            return response;
        }

        public string GetResponse(string URL)
        {
            StringBuilder response = new StringBuilder();

            try
            {
                SetupWebRequests(URL);
                req.Method = "GET";
                response = GetWebResponse();
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message, ex);
            }

            if (this._list_cookies)
            {
                ListCookieHeaders(URL);
            }

            return response.ToString();
        }

        public string GetResponse(string URL, bool UsePostData)
        {
            StringBuilder response;

            try
            {
                SetupWebRequests(URL);

                if (UsePostData)
                {
                    BuildPostData();
                }
                else
                {
                    req.Method = "GET";
                }

                response = GetWebResponse();
            }
            catch (Exception ex)
            {
                response = new StringBuilder(string.Empty);
            }

            if (this._list_cookies)
            {
                ListCookieHeaders(URL);
            }

            return response.ToString();
        }

		// test
        void SetCookieContainer(CookieContainer cc, string url)
        {
            IEnumerator ie = cookieko.GetCookies(new Uri(url)).GetEnumerator();
            while (ie.MoveNext())
            {
                string[] cookie = ie.Current.ToString().Split('=');
                cc.Add(new Cookie(cookie[0], cookie[1]));
            }
        }

        void ListCookieHeaders(string url)
        {
            System.Diagnostics.Debug.WriteLine("listing cookies");

            IEnumerator ie = cookieko.GetCookies(new Uri(url)).GetEnumerator();
            while (ie.MoveNext())
            {
                System.Diagnostics.Debug.WriteLine(ie.Current.ToString());
            }

            System.Diagnostics.Debug.WriteLine("listing cookies: done");
        }
        #endregion
    }
}
Click here to expand the blog post

Sound Fx Windows Mobile Application

A simple application that plays WAV files.

Download the application here

Discuss it here in XDA-Developers

Click here to expand the blog post

DropToWin v2.1

A small tool I made while creating my first ever custom Windows Mobile ROM.

What this tool basically do is to copy files or folder directly to Windows directory or import registry keys based from .REG and .PROVXML. It's a good tool actually if you are a WM modder like me, and make a lot of changes in your WM phone.

 

Here are the screen shots

 

Features:

  • Restart Sense/Manila
  • Stop Sense
  • Start Sense
  • Soft Reset
  • Copy Files and Folders without the nag of question to overwrite files/folders.
  • Can accept parameters - thanks to @ai6908 for his thoughts about this
  • Import .REF
  • Import .PROVXML
  • Execute the file selected from the list
  • Also embeded in the EXE itself is dotFred Task Manager v3.3

Parameters:

  • rs = Restart Sense
    ex: DropToWin.exe rs
  • ts = Stop Sense
    ex: DropToWin.exe ts
  • ss = Start Sense
    ex: DropToWin.exe ss
  • sr = Soft Reset
    ex: DropToWin.exe sr
  • copy = copy a file from source to desitination
    ex: DropToWin.exe copy \sourcefile.txt \windows\newfile.txt

Experimental File Parameters:

 

1. Deleting a file in \Windows directory directly here in DropToWin

Instructions:

-- 1.1 Make a dummy file

------ e.g. If you want to delete ManilaFull.xml in \Windows directory

------ 1.1.1 Create a dummy file and name it like this >> q=del;ManilaFull.xml

------ 1.1.2 Then copy that file where DropToWin.exe is located then click DROP button!

2. Renaming a file in \Windows directory directly here in DropToWin

Instructions:

-- 2.1 Make a dummy file

------ e.g. If you want to rename ManilaFull.xml in \Windows directory

------ 2.1.1 Create a dummy file and name it like this >> q=ren~New_File_Name_Of_ManilFull.xml;ManilaFull.xml

------ 2.1.2 Then copy that file where DropToWin.exe is located then click DROP button!

 

RELEASE HISTORY:

v2.1

  • ADDED
  • New "copy" parameter
  • Run the selected file
  • Import .REG and .PROVXML files
  • Install dotFred Task Manager v3.3

v1.6

  •  Fixed sync when loading large files.

v1.5

  • Added 2 new parameter
  • Added 5 shortcuts in Start Menu\Gambit Tools
  • Added HQ icons for those 5 shortcuts

v1.3

  •  Added parameter support
  •  Experimental file parameters

v1.2

  • Initial Release

Click here to expand the blog post

C# Run application and wait to exit

I've been using this sometimes and works as expected. For example, I want to run an application and wait for it to exit before going to the next statement or running another application, we could do it this way.

Requires System.Diagnostics

string windir = "\\Windows";
string sel = "application.exe";

ProcessStartInfo psi = new ProcessStartInfo()
{
	FileName = Path.Combine(windir, sel),
	UseShellExecute = true,
	WorkingDirectory = windir
};

Process p = new Process()
{
	StartInfo = psi,
	EnableRaisingEvents = true,
};

Debug.WriteLine("Executing " + sel + " and waiting for it to close");

p.Start();
p.WaitForExit();

Debug.WriteLine("Done. EXIT CODE" + p.ExitCode.ToString());

ProcesStartInfo is a class that we can use to instruct the Process class about the informations about the file that we want to execute, such as, what are the Parameters and the working directory

Process on the other hand is class that executes the file that we want to run and also contains some important methods about what we're trying to do here such as WaitForExit().

 

One great example of it is if you want to develop an application that will install a batch of CAB in Windows Mobile.

Click here to expand the blog post

Move DataGridView Row Up or Down

Standard DataGridView in Windows Forms does not support dragging rows so we had to make a simple code solution.

Below is a sample code demonstrating how to move the DataGridView Row going Up or Down

public Form1()
{
	InitializeComponent();
	InitializeControlEvents();
	InitializeControls();
	
	PopulateDataGridView();
}

void InitializeControls()
{
	songsDataGridView.MultiSelect = false;
	
	songsDataGridView.ColumnCount = 5;
	songsDataGridView.Columns[0].Name = "Release Date";
	songsDataGridView.Columns[1].Name = "Track";
	songsDataGridView.Columns[2].Name = "Title";
	songsDataGridView.Columns[3].Name = "Artist";
	songsDataGridView.Columns[4].Name = "Album";
}

void InitializeControlEvents()
{
	btnMoveRowUp.Click += new EventHandler(btnMoveRowUp_Click);
	btnMoveRowDown.Click += new EventHandler(btnMoveRowDown_Click);
}

private void PopulateDataGridView()
{
	string[] row0 = { "11/22/1968", "29", "Revolution 9", 
		"Beatles", "The Beatles [White Album]" };
	string[] row1 = { "1960", "6", "Fools Rush In", 
		"Frank Sinatra", "Nice 'N' Easy" };
	string[] row2 = { "11/11/1971", "1", "One of These Days", 
		"Pink Floyd", "Meddle" };
	string[] row3 = { "1988", "7", "Where Is My Mind?", 
		"Pixies", "Surfer Rosa" };
	string[] row4 = { "5/1981", "9", "Can't Find My Mind", 
		"Cramps", "Psychedelic Jungle" };
	string[] row5 = { "6/10/2003", "13", 
		"Scatterbrain. (As Dead As Leaves.)", 
		"Radiohead", "Hail to the Thief" };
	string[] row6 = { "6/30/1992", "3", "Dress", "P J Harvey", "Dry" };

	songsDataGridView.Rows.Add(row0);
	songsDataGridView.Rows.Add(row1);
	songsDataGridView.Rows.Add(row2);
	songsDataGridView.Rows.Add(row3);
	songsDataGridView.Rows.Add(row4);
	songsDataGridView.Rows.Add(row5);
	songsDataGridView.Rows.Add(row6);
}
		
void btnMoveRowDown_Click(object sender, EventArgs e)
{
	if (songsDataGridView.SelectedRows.Count > 0)
	{
		DataGridViewRow row = songsDataGridView.SelectedRows[0];
		int selRowIndex = row.Index;

		if ((selRowIndex + 1) == songsDataGridView.Rows.Count - 1) { return; }

		songsDataGridView.Rows.Remove(row);
		songsDataGridView.Rows.Insert(selRowIndex + 1, row);
		songsDataGridView.Rows[selRowIndex + 1].Selected = true;
	}
}

void btnMoveRowUp_Click(object sender, EventArgs e)
{
	if (songsDataGridView.SelectedRows.Count > 0)
	{
		DataGridViewRow row = songsDataGridView.SelectedRows[0];
		int selRowIndex = row.Index;

		if (selRowIndex == 0) { return; }

		songsDataGridView.Rows.Remove(row);
		songsDataGridView.Rows.Insert(selRowIndex - 1, row);
		songsDataGridView.Rows[selRowIndex - 1].Selected = true;
	}
}
Click here to expand the blog post

Enumerating InputMethods and Setting a Default InputMethod

Here's a simple way to enumrate InputMethods in Windows Mobile and also setting a default InputMethod.

 

Requres Microsoft.WindowsCE.Forms

InputPanel inpanel = new InputPanel();
List<InputMethod> list_im = new List<InputMethod>();

foreach (InputMethod im in inpanel.InputMethods)
{
	list_im.Add(im);
	MessageBox.Show(im.Name);
}

and setting a default InputMethods

// for example 0 is our FingerKeyb
// http://forum.xda-developers.com/showthread.php?t=501434
inpanel.CurrentInputMethod = list_im[0]

 

simple as that. I made an application for changing the default InputMethod and retains even after restart. 

 

It's called SIP Changer.

Below is the screenshot (click to enlarge)

SIP Changer will make a new file in Root named "SIP.txt" which contains the Name of the InputMethod as well as the GUID.

Instructions:

  1. Choose your InputMethod
  2. Click Set Selection as Default then a message will pop-up "Installed" meaning it also created a shortcut link in \Windows\StartUp folder

Parameters:

SIPChanger.exe <InputMethod name. case not sensitive>

e.g. SIPChanger.exe finGerKebD

 

and here's the file 

SIPChanger.exe (24.00 kb)

Click here to expand the blog post

Implementing JSON.NET in Windows Mobile NETCF 2.0 or 3.5

I was building an application in Windows Mobile and part of it is making a simple script which will be read upon running the application. As the script grows, it become more rubbish and tedious so I thought of changing the idea into something more beautiful and more readable. One thing that came off in my mind was XML, but immediately thought of JSON. So I done a little research if there are any libraries available for NET CF and turns out that the it self does supports NET CF.

 

There are some few problems though in the latest release which deserialization spits back an "MethodNotFound" error in DeserializeObject<T>() method which bugs me because the Doc it self said it supports Compact Framework. The error doesn't mean DeserializeObject<T>()  does not exists in Newtonsoft.Json.JsonConvert but inside that method. I got a little worried, but instead I look for an alternative, but luckily, the old version of JSON.NET is much "safer"(?). Thanks to Gunter Spranz for his work about his modified JSON.NET (skydrive link)

 

Here's a sample code I tried while doing some tests

using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using Newtonsoft.Json;
using System.Diagnostics;

namespace json_test
{
    class Program
    {
        static void Main(string[] args)
        {
            //string file = Path.Combine(Gambit.IO.Utilities.StartupPath(), "json.txt");
            //using (StreamReader reader = new StreamReader(file))
            //{
            //    file = reader.ReadToEnd();
            //}

            string file = @"{
	""wceload_param"": ""/delete 1 /noui /silent "{0}""",
	""final_message"": ""Installation is done. Have fun with Gambit ROM!"",
	""batch"": [
		{
			""file"": ""FingerKeyb-2.1.408.VGA.(Arrows).by.Giannis86.cab"",
			""isimportant"": ""true"",
			""friendly_name"": ""FingerKeyboard 2.1 with Arrows"",
			""ismarked"": ""true"",
			""unchecked_message"": """",
			""readme"": """"
		},
		{
			""file"": ""FingerKeybdDefault.lnk"",
			""isimportant"": ""true"",
			""friendly_name"": ""Set FingerKeyb as default SIP"",
			""ismarked"": ""true"",
			""unchecked_message"": """",
			""readme"": """"
		},
	]
}";

            script s = JsonConvert.DeserializeObject<script>(file);

            Debug.WriteLine(s.wceload_param);
            Debug.WriteLine(s.final_message);
            foreach (batch b in s.batch)
            {
                Debug.WriteLine(b.friendly_name);
            }

            script ss = new script();
            ss.final_message = "final message";
            ss.wceload_param = "wceload param";
            ss.batch = new List<batch>();
            ss.batch.Add(new batch()
            {
                friendly_name = "hello world"
            });
            ss.batch.Add(new batch()
            {
                friendly_name = "hello earth"
            });

            string json = JsonConvert.SerializeObject(ss, Formatting.Indented);
            Debug.WriteLine(json);
        }
    }

    public class script
    {
        public string wceload_param
        {
            get;
            set;
        }

        public string final_message
        {
            get;
            set;
        }

        public List<batch> batch
        {
            get;
            set;
        }
    }

    public class batch
    {
        public string file
        {
            get;
            set;
        }

        public string isimportant
        {
            get;
            set;
        }

        public string friendly_name
        {
            get;
            set;
        }

        public string ismarked
        {
            get;
            set;
        }

        public string unchecked_message
        {
            get;
            set;
        }

        public string readme
        {
            get;
            set;
        }
    }
}

 

in case the file doesn't exists any more in Gunter Spranz's Skydrive, you can download that file directly in this site

Json.Net CF 2.0.zip (205.50 kb)

Click here to expand the blog post

About Jayson Ragasa

Jayson Ragasa is an Applications Developer and founder of PinoyTouchTech.

Badges

Free Page Rank Tool

wall.jaysonragasa.net Webutation
Uptime Report for http://wall.jaysonragasa.net/: Last 30 days