Wednesday, February 27, 2008

Document Mapper and Attenex in the News

I worked on Document Mapper at Attenex for six years. Document Mapper is an application designed to help lawyers find relevant information scattered in hundreds of thousands of electronic documents. I am very proud of my involvement with Document Mapper, and it is very nice to see it in the news. I wonder what happened to those emails... ;-)

Monday, February 25, 2008

Calling a webservice from Silverlight 1.1

I was going to write a post about how to call a webservice from Silverlight 1.1. But then I found a great link explaining it way better than I can. I have followed each step, and it works like a charm.

Here is the link: Integrating Web Services and Silverlight including Debugging

Because one of the reason I am making this blog is to catalog how to do things (so I don't forget), I am going to copy the contents of this post to this blog. But this is just for my purposes. If the above link works, please follow it, if not, here is the data in a less pretty form. Thanks Peter.

----------------------------------------------------------------------------------

Integrating Web Services and Silverlight including Debugging (Silverlight .net 1.1 Alpha)

The Short Story:
So, I'm working on a Silverlight .net project where the main source of data is a webservice. It's not a huge amount of data, but it is not small either. In general it's about 20K to 50K per download. My original plan was to use the quickstart method (POX) and basically parse the xml using the XMLReader classes that are available on Silverlight. That didn't work so well for me, so I switched to the proxy classes and did not have much more luck. The primary problem is I could not figure out how to debug the code on the Silverlight side. Ultimately, I ended up figuring out how to do debugging and in the next few paragraphs I'll go through the steps including screen shots showing how to do it.

The Solution
So, let's start from the beginning. First thing to do is create an empty solution. In my case, I'm going to call that solution SearchLightMain. I'm going to use Orcas rather than vs2005 because that is required for Silverlight. Ultimately, the web services project will deploy on .net 2.0 but the development envirnoment is still Orcas. (File/New Project/Other Project Types/Empty Solution)

The Silverlight Side
Next, right mouse on the solution explorer and choose Add/New Project/C#/Silverlight. Leave your solutions directory in the Location textbox and put the name of your silverlight project in the name field. In my case, the name will be SearchLight.

After the project is created, my preference is to add a textblock to the page.xaml file and some color to the background so when I run it I can tell it is actually working. Here is what it looks like now after I've done that if right mouse and run TestPage.html.

Now you have the silverlight project done. Let's create the Web Service Project now inside of the same solution we made first.

The Web Service
On the solution explorer, right click on the top of the tree (solution) and say "Add/New Website". Choose ASP.NET web site. (remember, this is Orcas we are still in). Make it a "File System", "C#" project. and for the location put the directory where you have your empty solution followed by the name \WebService. In my case, the name is: C:\Users\pkellner\Documents\Visual Studio Codename Orcas\Projects\SearchLightMain\WebService Also, change the framework to .NET Framework 2.0 in the upper right hand corner of the screen "Add New Website".

Now, we need to make the web service. The easiest thing to do is grab the code from the quickstart and paste it into a webservice if you don't have your own. The URL where you can grab the code is: http://www.silverlight.net/QuickStarts/Remote/UsingJSON.aspx

One thing I found was that if you downgrade to .net 2.0 from .net 3.5 as we did earlier, the webservices that use the tag [System.Web.Script.Services.ScriptService] do not work. My solution was to create an Ajax enabled web site with vs2005 and copy that web.config into this project.

Continuing on, the next thing I did was to copy the files TestPage.html, TestPage.html.js and Silverlight.js from the SearchLight project into the WebService project. This is because we will actually be running the project from the webservices project.

Finally, to complete the linking, right click on the webservice project and select "Add Silverlight Link…" (not sure what the … means).

Now, do a Build/Rebuild all and the xaml files from your silverlight project as well as the ClientBin directory will be imported (copied) into your web services project.

Now, verify that your web service actually works by right mousing on the asmx file and pressing browse.

The next step is not what you are expecting. The quickstart says go ahead and create a web reference and run that. That's great as long as you don't want to run this project on a real server (port 80). Best I can tell, it hardwires the port to whatever the debugger is set to. I tried without much luck to change the property in the localhost control created but without much luck. I believe a better solution is to run the tool called slwsdl.exe which is part of the orcas distribution. essentially, you run it with the /SilverlightClient option and point it at your web service.

This created a file called WebService.cs which I put in my silverlight project. It becomes my proxy. The nice thing is I can modify the line that reaches out to the webservice to be whatever I want. By default it is the local server with the debug port set, but when I deploy to a real server I can change that address to the real name.

Finally, now you can debug.

Go to your SearchLight project and add some code to the Page.xaml.cs file. then, run the project from the webservice/testpage.html and vwala! You can debug.

Final thoughts
So, why go through all this trouble? It think I forgot to mention that it's to avoid the dreaded Cross-Domain issue. That is, your Silverlight client can not call a webservice that is not in the same root domain and running on the same port as your silverlight app. Also, while I was writing this article to help others avoid some of the pain I've been through, I put a project together from scratch. A couple things I noticed along the way.

* When you copy xaml files from one project to another, you can no longer run them. You need to change their property from Silverlight Page to Embedded Property or the page can not be read in it's codebehind.
* It's very important to go to debug/exceptions and turn on all exceptions, otherwise you will not know what is killing your app (see previous item).
* It's a cold cruel world out there on the bleeding edge.

Good luck with your Silverlight development. There are lots of opportunities out there. Indeed, a target rich environment.

Sunday, February 24, 2008

Writing a simple c# WebService

If you want to save state from Silverlight between sessions, you will need access to the server’s file system or database. To access either, you will need to create a web service.

In later posts I will discus how to USE the web service from within Silverlight, but for now, this is just how to make a web service in .NET.

Create a new class that extends System.Web.Services.WebService:

using System.Web.Script.Services;

public class ExampleWs : System.Web.Services.WebService



Add four attributes to the class:

[WebService(Namespace = "http://yourSiteName.com/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[ToolboxItem(false)]
[ScriptService] //To allow this Web Service to be called from script, using ASP.NET AJAX
public class ExampleWs : System.Web.Services.WebService



Create a simple method. You want your web methods to be as simple as possible. Don’t make a habit of doing business logic from within a web method, instead, simply call a function from your business logic’s dll.
Here, we will just return the string “Hello World”:

[WebMethod] //attribute needed for method to be visible as a web method
[ScriptMethod] //To allow this Web Method to be called from script, using ASP.NET AJAX
public string HelloWorldExample(bool allCaps)
{
if(allCaps)
return “HELLO WORLD”;
return “Hello World”;
}


From the web service, you are out of the Silverlight sandbox and can access the file system of the server.
To read the files in a directory:

[WebMethod] //attribute needed for method to be visible as a web method
[ScriptMethod] //To allow this Web Method to be called from script, using ASP.NET AJAX
public string[] GetFileNames(string dir)
{
string[] files = {""};
try
{
string path = Server.MapPath(dir).ToString(); //get the path on the server
files = System.IO.Directory.GetFiles(path);
return files;
}
catch (Exception)
{
return files;
}
}


If you publish this web service to a web site, you can call it by simply going to www.yourwebsite.com\ExampleWs.asmx

Saturday, February 23, 2008

Access ASP page from Silverlight 1.1 control

Say you want to alter the contents of an ASP page from within a Silverlight control on the page. To do so, get the HtmlDocument from HtmlPage and use GetElementById:


//This hides the div with id=contentId and displays the div with id=content2Id
HtmlDocument document = HtmlPage.Document;
document.GetElementByID("contentId").SetStyleAttribute("display", "none");
document.GetElementByID("content2Id").SetStyleAttribute("display", "block");

Wednesday, February 20, 2008

A game loop for Silverlight 1.1 using a Storyboard

So you want to have a timer for your Silverlight game. The current option is to use the Storyboard as a timer. I got most of this code form a very good post from here.

Here is the gameloop class:

/// <summary>
/// Animation on a storyboard used as a timer tick for a game loop
/// from: http://silverlightrocks.com/community/blogs/silverlight_games_101/archive/2007/05/21/a-better-game-loop.aspx
/// </summary>
public class Gameloop
{
/// <summary>
/// canvas for update
/// </summary>
Canvas targetCanvas = null;

/// <summary>
/// story board for animation
/// </summary>
Storyboard storyboard;

/// <summary>
/// datetime of last update
/// </summary>
DateTime lastUpdateTime = DateTime.MinValue;

/// <summary>
/// Delegate used to pass tick event
/// </summary>
/// <param name="ElapsedTime">time since last tick</param>
public delegate void UpdateDelegate(TimeSpan ElapsedTime);
/// <summary>
/// Tick event
/// </summary>
public event UpdateDelegate Update;

/// <summary>
/// Create a storyboard gameloop for the canvas
/// </summary>
/// <param name="canvas">The canvas to update on tick</param>
public void Attach(Canvas canvas)
{
targetCanvas = canvas;
storyboard = new Storyboard();
storyboard.SetValue<string>(Storyboard.NameProperty, "gameloop");
canvas.Resources.Add(storyboard);
lastUpdateTime = DateTime.Now;
storyboard.Completed += new EventHandler(storyboard_Completed);
storyboard.Begin();
}

/// <summary>
/// stop the rimer and remove the canvas
/// </summary>
/// <param name="canvas">the canvas to remove</param>
public void Detach(Canvas canvas)
{
storyboard.Stop();
canvas.Resources.Remove(storyboard);
}

/// <summary>
/// Tick event for animation on timer
/// </summary>
/// <param name="sender">not used</param>
/// <param name="e">not used</param>
void storyboard_Completed(object sender, EventArgs e)
{
TimeSpan elapsedTime = DateTime.Now - lastUpdateTime;
lastUpdateTime = DateTime.Now;
if (Update != null) Update(elapsedTime);
storyboard.Begin();
}
}


In your main canvas, create the gameloop and give it a function to call on update:

//create timer
Gameloop mGameloop = new Gameloop();
mGameloop.Attach(this);//attach the canvas that you want a timer for
//assign a callback function to be called on tick
mGameloop.Update += new Gameloop.UpdateDelegate(gameloop_Update);


Here is my update function:

/// <summary>
/// each tick this methos is called
/// </summary>
/// <param name="ElapsedTime">time since last tick</param>
void gameloop_Update(TimeSpan elapsedTime)
{
//do something like move a sprite on the screen
mSprite.Animate(elapsedTime);

//force garbage, not necessary, but may help with the beta(1.1)
GC.Collect();
}


I have found that this approach does not work well if you have other Storyboards in your application. In this case, you would need to manage which Storyboard is running and only let one run at a time. If you do not manage your Storyboards, you will get odd effects.

Tuesday, February 19, 2008

Load a file Asynchronously with Silverlight 1.1

Say you want to load some or all of the images for your new Silverlight game up front. Instead of Silverlight loading a image file when it is about to use it, we load the files before we begin the game.

To do this, we use the System.Windows.Downloader.

Set up our member variables:

/// <summary>
/// TestBlock to show progress
/// </summary>
TextBlock mProgressText = new TextBlock();

/// <summary>
/// Downloaded to do the download
/// </summary>
Downloader mImageDownloader = new Downloader();


Ready our scene and set up the Downloader:

/// <summary>
/// Using the mImageDownloader, start to load a file from the filesystem (on the server)
/// </summary>
/// <param name="fileName">file to load</param>
void LoadImagesAsynchronously(string fileName)
{
//set up progress label
mProgressText.Text = "Loading Images [--------------------------------------------------]";
mProgressText.SetValue(Canvas.ZIndexProperty, 100);//move above whatever else is on the canvas
this.Children.Add(mProgressText);

//set up completed event
mImageDownloader.Completed += new EventHandler(ImageDownloader_Completed);

//set up progress changed event
mImageDownloader.DownloadProgressChanged += new EventHandler(ImageDownloader_DownloadProgressChanged);

//set which file to load
mImageDownloader.Open("GET", new Uri(fileName, UriKind.Relative));

//start the download
mImageDownloader.Send();
}


Set up our progress changed event:
As the downloaded downloads the file, it reports back on its progress, and we report that back to the user via mProgressText.

/// <summary>
/// show progress of loading images
/// </summary>
/// <param name="sender">mImageDownloader</param>
/// <param name="e">eventArgs from downloader</param>
void ImageDownloader_DownloadProgressChanged(object sender, EventArgs e)
{
//every time the downloader updates its progress, update the textplock to show progress
//like: "Loading Images [IIIIIIIIIIII--------------------------------------]";
int tickCount = 50;
string newText = "Loading Images [";
for (int i = 0; i < tickCount; i += 3)
{
if (i < mImageDownloader.DownloadProgress * tickCount)
{
newText += "IIIIII";
}
else
{
newText += "---";
}
}
newText += "]";
mProgressText.Text = newText;
}


Set up our complete event:

/// <summary>
/// fires after images load
/// </summary>
/// <param name="sender">mImageDownloader</param>
/// <param name="e">eventArgs from downloader</param>
void ImageDownloader_Completed(object sender, EventArgs e)
{
//update the progress indicator one last time
//you would probably remove the indicator at this point
//and continue on with your application code
mProgressText.Text = "Load Complete!";
}


The Downloader can download a single file. You can download one file or zip which can contain many files.

To access the files:

/// <summary>
/// assigns an image from the downloader to a geometry path
/// </summary>
/// <param name="imageName">image name in downloader</param>
/// <param name="geometryPath">the path to assign the image to</param>
public void SetGeometryImage(string imageName, Path geometryPath)
{
ImageBrush ibrush = new ImageBrush();
//access the image in the downloader by file name i.e. "Image.jpg"
ibrush.SetSource(mImageDownloader, imageName);
this.GeometryPath.Fill = ibrush;
}

Monday, February 18, 2008

How to read and write simple XML with Silverlight 1.1

Silverlight does not have access to XmlDocument so we must make use of the XmlReader and XmlWriter.

To write:

/// <summary>
/// Write data to XML in Silverlight 1.1
/// </summary>
/// <returns>string in XML format</returns>public string WriteXML()
{
StringBuilder sbuilder = new StringBuilder();
XmlWriter writer = XmlWriter.Create(sbuilder);//create an XMLWriter from a StringBuilder
writer.WriteStartDocument();//start xml document tag

//<outerElement>
writer.WriteStartElement("outerElement");

//<innerElement>
writer.WriteStartElement("innerElement");

//<attribute1> of inner element
writer.WriteStartAttribute("attribute1");
writer.WriteString("value of attribute1");
writer.WriteEndAttribute();

//<attribute2> of inner element
writer.WriteStartAttribute("attribute2");
writer.WriteString("value of attribute2");
writer.WriteEndAttribute();

//</innerElement>
writer.WriteEndElement();

//</outerElement>
writer.WriteEndElement();

writer.WriteEndDocument();//end xml document tag
writer.Flush();

return sbuilder.ToString();
}

This will output:

<?xml version="1.0" encoding="utf-16"?>
<outerElement>
<innerElement attribute1="value of attribute1" attribute2="value of attribute2" />
</outerElement>

To read:

/// <summary>
/// Read data from XML in Silverlight 1.1
/// </summary>
/// <param name="xmlString">string in XML form to read</param>
public void ReadXML(string xmlString)
{
TextReader stream = new StringReader(xmlString);
XmlReader reader = XmlReader.Create(stream);//create an XML reader
bool hasMore = reader.Read();
while (hasMore)//read till we get to the end
{
//lets just find the inner element and read its attributes
if (reader.IsStartElement() && reader.Name == "innerElement")
{
string at1 = reader.GetAttribute("attribute1");
string at2 = reader.GetAttribute("attribute2");
//todo: do something with the attributes...
}
//if we had other data, we would have logic for each value we wanted to read
hasMore = reader.Read();
}
}

ReadXML(WriteXML()); will read “value of attribute1” and “value of attribute2” (but do nothing with the data).

For readability I have used hard coded string values. For production code, you should define a schema of consts or an enumeration for all element names.

Hello World

This is my first blog post. In the future, they will be slightly more interesting.

I am using the css code from Ishan