Showing posts with label Silverlight. Show all posts
Showing posts with label Silverlight. Show all posts

Saturday, 6 August 2011

Passing Objects between Silverlight Applications

This week I have been making some prototype applications using Silverlight, which has been awesome, and I decided that I wanted to pass a few objects between these applications. If this wasn't a quick prototype and didn't have to run off of a Sales person's laptop I would have whipped together a quick bit of RIA Services and got them talking that way. However these are just two out of browser applications.


Enter Local Messaging.

Silverlight has a great little feature, that I'm not sure is too often used, Local Messaging. Local Messaging allows you to send simple string messages between two Silverlight applications running on a local computer. This can be within the same Web Page or two Out of Browser Applications


Local messaging works by having a LocalMessageReciever listening for messages in one application and then a LocalMessageSender sending messages from the other. If you want two way communication you simply have a listener and sender in each application.



// In the receiving application:
LocalMessageReceiver messageReceiver = new LocalMessageReceiver("receiver");

// In the sending application:
LocalMessageSender messageSender = new LocalMessageSender("receiver");

I'm not going to explain how you send messages between the applications as MSDN does a far better job of doing so. However MSDN only talks about simple string messages, I wanted to send objects.

Serialising the objects

The obvious solution I thought was to just serialise the objects to a string and then back at the other end. Silverlight doesn't support binary serialisation and as we have to send the message as a string the Serialisation could be XML, which is common practice and I'm sure most people know instinctively how to do so, however XML is bloated. Alot of our message string would be information we aren't interested in. Now in most applications we don't worry about this and XML is fine however this local app messaging has a maximum string size of 40 kilobytes. We don't want to fill this up with XML notation, instead we want something alot leaner.


Although Silverlight doesn't support Binary Serialisation it does however support JSON. JSON is a far less verbose markup which will mean our messages will be more about the object data and structure rather than syntax which is a good thing


When it comes to JSON serialisation there are a few options, the built in DataContractJsonSerializer , JSON.Net etc. For the ease of setup I have gone for using the build in DataContractJsonSerializer. Here i take a generic object and serialise it to a stream which we can then use in our message.


DataContractJsonSerializer serialiser = new DataContractJsonSerializer(message.GetType());
var stream = new System.IO.MemoryStream();
serialiser.WriteObject(stream, message);

With this string we can simply use a streamreader to get the string that our sendasync message sender method requires.


 new System.IO.StreamReader(stream).ReadToEnd())

But there's a problem, we cant just send the json object, as at the other end we won't know the type. What we have to do then is send the type and the serialised form. This leaves us with the following:


stream.Position = 0;
sender.SendAsync(String.Format("{0}|!|{1}", message.GetType(), new System.IO.StreamReader(stream).ReadToEnd()));

Note: I have used |!| as a way to know where the type name ends and the stream begins, I could have said {type}={jsonserilaisedobject} but I chose not too, for neatness though this should be possible I just chose to use a delimiter I knew wouldn't appear in my serialised object.

Receiving the message and getting the object back

To get the object back in the other application, after receiving the message from the sender we have to split the string based on the delimeter and then deserialise the object.


var type = e.Message.Substring(0, e.Message.IndexOf("|!|"));
var data = e.Message.Substring(e.Message.IndexOf("|!|") + 3);

The DataContractJsonSerializer requires a stram not a string to deserialise an object so next take the data string and turn it into a stream. Then just deserialise.


var stream = new System.IO.MemoryStream(System.Text.Encoding.UTF8.GetBytes(data));
var serialiser = new DataContractJsonSerializer(Type.GetType(type));
if(type == "OurCustomType"){
    var result = (OurCustomType)serialiser.ReadObject(stream);
}

And that's it, obviously handling lots of types would mean not using an if statement, maybe a switch or whatever but the principle is the same.


I hope you find this of use, it certainly made my prototype far more functional and proved a neat way of doing it

Saturday, 15 January 2011

Windows Phone Development and Designer Exceptions

I've been building a few Windows Phone applications recently and have been learning lot's along the way and am hopefully releasing a couple of these in the near future. However on my current project I have been constantly hounded by the designer view of my xaml pages throwing exceptions.


Tonight I decided to finally look into why these occur, I have managed to ignore them previously due to the weird nature of them. My exception scenario consists of a page that contains a pivot control and inside one of the pivot items there is a user control. The user control contains a simple list view which has some data bound to it

Now I have always got past the exceptions as if you use the designer view for the user control it works fine, however as soon as I put it into a page or a pivot the designer starts kicking off.


The actual exception is as follows:

Could not load type 'System.Net.HttpUtility' from assembly 'System.Windows, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e'.
It then goes on to tell me the line number and location etc of this. At first glance this exception looks a bit weird as in the code view and if you run the application the exception is never thrown.

The first thing I did to try and resolve this issue was to find where System.Net.HttpUtility lives, in the desktop version of Silverlight it lives in System.Windows.Browser But as the Windows Phone version of Silverlight doesn't include System.Windows.Browser the development team moved the httputility type into the System.Net namespace and put it into the System.Windows assembly, http://msdn.microsoft.com/en-us/library/dd470087(v=vs.95).aspx#Assemblies. This explains why the project builds and runs etc, however it doesn't explain why the designer has such a bad time with it.


Sadly I couldn't find a source or information on this exception, my guess is the designer is loading an older version of the assembly, a missing designer tool update from the RTM or something similar {Edit see my update below}. However what I have found is that Silverlight does have another way of encoding and decoding URL strings. System.Uri.EscapeUriString, if you use this the phone and the designer all work happily. A minor change and a headache eased :) I'm hoping the next tools update fixes the issue but until then this seems the safest method of escaping url strings.

Update

As I oddly found this issue intriguing I decided to investigate further. I opened another Visual Studio attached it to the Visual Studio process I had my project in and turned on catch all exceptions. Then when the exception was thrown I looked at the AppDomains assembly list, System.AppDomain.CurrentDomain.GetAssemblies(), 55 in all were loaded. I then looked through this list for the assembly referenced in the exception. This assembly then had the following location value: Location "c:\\Program Files (x86)\\Microsoft Silverlight\\4.0.51204.0\\System.Windows.dll". This is the main Silverlight 4 runtime, NOT the Windows Phone version which is 3.7.x, to be certain I then opened the assembly in Reflector to be sure the System.Net.HttpUtility was missing and it was.

So as suspected the designer is loading the wrong assembly thus the error, the assembly it should be loading is in the referenced assemblies folder, C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\Silverlight\v4.0\Profile\WindowsPhone\System.Windows.dll , hopefully reporting this on Connect will result in a hotfix / update.