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

Tuesday, March 31, 2009

ActualWidth,ActualHeight returning ZERO

In the development of Silverlight it happens often that you don't assign any value to Width and Height of UI Elements in your code,then in run time those values will be referenced.

In that case ActualWidth and ActualHeight must be something you refer to.But if you write code like below,an unpredictable incident may happen to you.


public Page()
{
InitializeComponent();

//Set the location of the button around the corner of the canvas.
Canvas.SetTop(imageButton, 5);
Canvas.SetLeft(imageButton, LayoutRoot.ActualWidth - 25);
}
In the code above ActualWidth of the root canvas is referenced to put the button near the corner of the canvas.But the code fails to get the anticipated value.Actually it returns zero value.So I moved the lines to Loaded event handler,but it didn't make any difference.

At first, I was quite at a loss what should I do.But I went back to basics and referenced documents again,I found the solution.

Value of ActualWidth and ActualHeight are given after layout calculation process has completed.And the process is executed asyncronously ,which means the calculation process is registered in queue of the related thread at the moment when changes have been made to the UI elements.

Then after all lines in a current block executed,the calculation process will begin.

So What to do is to put "referencing ActualWidth and ActualHeight" also in the same queue,which means you should write code like this.


public Page()
{
InitializeComponent();

Dispatcher.BeginInvoke(
() =>
{
Canvas.SetTop(imageButton, 5);
Canvas.SetLeft(imageButton, LayoutRoot.ActualWidth - 25);
}
);


}
BeginInvoke method in Dispatcher in Silverlight API doesn't have paremeter to designate priority.So I think the order you add delgates determines the order of execution, which turns out "Referencing ActualWidth" always comes after layout calculation process in the code above.

I just wanted to make sure my assumption about order of execution,I wrote and run the code below several times.


Dispatcher.BeginInvoke(
() =>
{
Debug.WriteLine("1");
}
);

Dispatcher.BeginInvoke(
() =>
{
Debug.WriteLine("2");
}
);

Dispatcher.BeginInvoke(
() =>
{
Debug.WriteLine("3");
}
);
Numbers were displayed in order "123" all the time,that was what I had anticipated.

I am not a native english speaker,so if my explanation is not good enough for you to understand, Plaese let me know ,I will be grateful.

Monday, March 30, 2009

Making a Silverlight app getting data from Google app Engine


Recently I am interested in Silverlight2 and Google App Engine.
So I've made a simple Silvelight2 app getting data from a mod on Google App Engine.

I adopted REST Protocol in communication between Silverlight2 and App Engine.

Here is the Server-Side code written in Python,about which you don't have any other choice.


#!-*- coding:utf-8 -*-
from google.appengine.ext import webapp
from google.appengine.ext.webapp.util import run_wsgi_app

class MainPage(webapp.RequestHandler):
def get(self):
self.response.headers['Content-Type'] = 'text/xml'
self.response.out.write('<?xml version="1.0" encoding="UTF-8" ?>')
self.response.out.write('<Person>')
self.response.out.write('<LastName>Jackson</LastName>')
self.response.out.write('<FirstName>Michael</FirstName>')
self.response.out.write('</Person>')

application = webapp.WSGIApplication(
[('/', MainPage)],
debug=True)

def main():
run_wsgi_app(application)

if __name__ == "__main__":
main()

The code above simply returns XML format string.

XML Format:


<?xml version="1.0" encoding="UTF-8" ?>
<Person>
<LastName>Jackson</LastName>
<FirstName>Michael</FirstName>
</Person>
Next is the Client-Side code.

Silverlight2 has XML Serialize API. With it you can Deserialize XML String to a C# Class insntance.

So I defined Person class in first that corresponds to the XML format.

Person.cs:

public class Person
{
public String LastName { get; set; }
public String FirstName { get; set; }
}
The following code makes an Asynchronous http request.
WebRequest class in Silverlight doesn't have GetResponse() which is synchronous version of BeginGetResponse() existing in .NET Framework.


private String _svrUrl = "http://localhost:8080/";

private void OnLoaded(Object sender, RoutedEventArgs e)
{
try
{
WebRequest req = WebRequest.Create(new Uri(_svrUrl));
req.Method = "GET";

req.BeginGetResponse(new AsyncCallback(GetResponseCallback), req);
}
catch { }
}
The callback method is called when the server return a response,
then I start reading data from the response stream asynchronously using ThreadPool class.


private void GetResponseCallback(IAsyncResult ar)
{
try
{
WebRequest req = ar.AsyncState as WebRequest;
WebResponse res = req.EndGetResponse(ar);

Stream stream = res.GetResponseStream();

ThreadPool.QueueUserWorkItem(new WaitCallback(ReadContents), stream);
}
catch { }
}
In the last part,we deserialize the person class instance from the XML Stirng read from stream.

Be sure to use Dispather accessing to a control in a code block executed by a diffrent thread.


private void ReadContents(Object stream)
{
Person person = null;

using (StreamReader reader = new StreamReader(stream as Stream))
{
XmlSerializer serializer = new XmlSerializer(typeof(Person));

person = serializer.Deserialize(reader) as Person;

}

Dispatcher.BeginInvoke(
() => {
_lastName.Text = person.LastName;
_firstName.Text = person.FirstName;
}
);
}