Feeds:
Posts
Comments

Archive for the ‘Silverlight tips and tricks’ Category

Here’s a tip to get the better performance when sending xml to a WCF web service from Silverlight or WPF…

Never send or return xml as a string. Anytime you pass around a string data type as a parameter to your service or send back a string return value, the string will be XML encoded. Imagine, the following string:

<hello/> (8 bytes)

this is really sent around as:

&lt;hello/&gt; (14 bytes)

Something that should have taken only 8 bytes of your bandwidth, took almost double that. Now an extra 6 bytes isn’t bad, but you can see how it wouldn’t take long for your xml to quickly inflate the overall size of the request or response and at some point even have a noticable delay for the usability of your app.

Fortunately, the solution is simple! Use an System.Linq.Xml.XElement object instead. By doing so, WCF is smart enough to encode your xml right in the message envelope as pure xml. Furthermore, if you need to work with that xml on the receiving end, it’s already in an object type more suitable for most purposes than a String.

To demonstrate and prove what is happening, I wrote a test app that sent a test parameter to a service as a String, XElement and byte array. The test data being sent in all three cases was the xml: <test/>. Then I used fiddler2 web debugging proxy to see what was actually sent to the server. Check out below what I saw:

Sending a String:

<s:Body><DoWorkString><param1>&lt;test/&gt;</param1></DoWorkString></s:Body>

Sending a byte array:

<s:Body><DoWorkByteArray><param1>PHRlc3QvPg==</param1></DoWorkByteArray></s:Body>

Sending an XElement:

<s:Body><DoWorkXElement><param1><test /></param1></DoWorkXElement></s:Body>

XElement wins! 🙂 And just to be sure, I also confirmed that responses for the three data types produced identical results.

 

A note about compression

Compression does not completely remove the benefit of sending as XElement. Besides the fact that server compression only works on responses, it doesn’t eliminate the benefit from sending xml without encoding. This was surprising to me. I thought that compressing my responses on the server would find common xml encoding phrases like “&lt;” and “&gt;” and find a way to turn them into single bytes using a mapping technique and make an xml encoded and non-xml encoded response virtually identical in size when compressed. To test my assumption, I ran a test where I took a big xml file, and added it and an encoded version of it in a zip file. Here was my result:

encoded and decoded compression results

Acording Although encoded xml can be compressed at a slightly higher compression ratio, it was not as dramatic as I thought and suggests and the final compressed sizes show that although compression on the server helps reduce the size of your response a great deal, xml encoded strings will still be larger than necessary. Check out my previous blog post to find out more about opimizing responses by turning on server compression.

Read Full Post »

Keep your Silverlight app running fast by compressing your service responses. Imagine you’re downloading 1MB worth of text. Compressed, that same text can usually be reduced to under 200K. This reduction can be significant enough to be noticable to even clients on good internet connections and over time will save you money on bandwidth usage.

Fortunately, there’s NO need to find a 3rd party zip component or to try to do it yourself. IIS has everything all built right in, you just need to enable it.

The way it works is, the browser sends up an http header of “Accept-Encoding” with gzip and/or deflate as the value with each request to your WCF service. As long as IIS is configured correctly, the server will automatically compress the response from the service and the client will automatically decompress it before your code enters the picture. Not a single line of code is required on our part to take full advantage of this built in compression feature that works across most major browsers.

To set up IIS6 to participate you need to (sorry, haven’t tried this on IIS7):

1) In the IIS console, right click on “Web Sites”, choose properties, select the Services tab and check “Compress application files”

iis

2) Also in the IIS console, go to the Web Service Extensions folder and click the “Add a new Web service extension” link. In the dialog that appears, enter a name for the extension. I named mine “gzip”. Next, enter the path of the dll capable of zipping the responses (c:\windows\system32\inetsrv\gzip.dll), and check “Set extension status to Allowed”.

extension1

3) Run the following command lines to update metabase.xml:

CSCRIPT.EXE ADSUTIL.VBS SET W3Svc/Filters/Compression/GZIP/HcScriptFileExtensions “asp” “dll” “exe” “svc”
CSCRIPT.EXE ADSUTIL.VBS SET W3Svc/Filters/Compression/DEFLATE/HcScriptFileExtensions “asp” “dll” “exe” “svc”
CSCRIPT.EXE ADSUTIL.VBS SET W3Svc/Filters/Compression/GZIP/HcDynamicCompressionLevel 9
CSCRIPT.EXE ADSUTIL.VBS SET W3Svc/Filters/Compression/DEFLATE/HcDynamicCompressionLevel 9

4) Restart IIS (you can right click on the computer name in the IIS console, choose All Tasks, and select “Restart IIS”). Not 100% sure this is necessary.

5) Wait. It took my server on Amazon EC2 approximately 3 minutes before the changes from step #2 flowed to the metabase.xml file. You can always go check by looking at the date modified of c:\windows\system32\inetsrv\metabase.xml

In the end, you can test that it’s working by going to PipeBoost and typing in the url of your .svc file. Also, when running your app, you can use a tool like fiddler2 to show you the data actually coming down to the client along with an indicator that it is compressed.

That’s it, don’t do a think to your Silverlight app but watch it instantenously start downloading data faster!

Read Full Post »

ASP.NET allows you to get at some great information about the client and the server via the HttpContext.Current.Request.ServerVariables collection. Likewise, Silverlight allows you to get at a few local variables of its own through the System.Windows.Browser.HtmlPage.BrowserInformation object.

But, to use these variables we often need to know what kind of values to expect. For example, let’s say you’re going to create a condition based on which browser the user is using. You would use BrowserInformation.Name. But Name is a string, not an enum. So what are the various values that can be returned by this property? This might be documented somewhere for the officialy supported browsers, but the only fool proof way is to actually try it by writing a dummy Silverlight app that spills out this variable and run it in all the different browsers to see what comes back. The same applies to ServerVariables but even more so because this is just a big dictionary so you don’t even know which variables are going to be present let alone what their values will be.

Here’s a utility I wrote for anyone to use that will help you look at all the BrowserInfo properties and ServerVariables. Hit this page from any machine to see what values it is sending up to the server. Bookmark this page, it will probalby come in handy someday when you’re scratching your head wondering what useragent you’re sending up to the server.


BrowserInfo and ServerVariables

Another cool part is that it not only shows you what servervariables are available at the time your web page is requested, but also what servervariables are available when you hit a WCF service from Silverlight. There are some subtle differences.

Also note that it’s hosted on Azure so you can also get a glimpse of which ServerVariables Azure provides access to. On first glance it looks the same as Windows Server but I haven’t done a variable by variable comparison.

Enjoy, I hope this comes in handy!

Download the source code here to see how it works or to host on your own server.

Read Full Post »

Here’s a fun and useful UI control I suspect many developers out there are writing over and over again. It’s essentially a border control with an independent autosizing header. It’s not included in the framework, toolkit, or any of the open source libraries that I’ve seen and I’ve needed it myself on more than one occasion so I thought I’d share and save everyone a little work.

Silverlight Border control with a header

Click to run


Here an example of it in it’s more basic form.

The code to use it is simple:

<pp:HeaderBorder Header=”Header” Content=”Content goes here”/>

You can control the radius, border, and individual backgrounds of the two main containers. The header and content areas are just standard containers so you can put any type of content in them — like all good Silverlight controls should allow. 🙂

To create this control, I sandwiched a line in between two border controls (the top containing the header ContentPresenter and the bottom containing the main ContentPresenter). Wrapped around the 2 borders and line controls is another Border control.

I found two different techniques to make sure the inner border controls don’t blead out over the containing border control (which is really the only modestly complex part about this control).

  1. In the first technique, I was able to clip the inner contents using a RectangleGeometry control with RadiusX and RadiusY matching that of the outer border
  2. As an alternate technique I set the individual pieces of the CornerRadius property on the two inner border controls. For example, the top might be set to “6,6,0,0” and the bottom: “0,0,6,6”. This made them match the containing border control’s border and not extend beyond it.

The rest of the work is just putting it in a control and wiring up the properties with the xaml in generic.xaml so the control’s consumer can control it.

Download the source code and check out both techniques if you’re curious or just throw it in your app and start using it.

Happy Silverlight coding!

Read Full Post »

Here’s a good looking, light-weight GroupBox control for Silverlight 2 RTW.
Silverlight GroupBox Control

Download source code for VB.NET
Download source code for C#
Download the binary

According to Shawn Burke on the Microsoft Silverlight Toolkit Team, “groupbox is on the list but not in this control set

So until then, here’s a fully functional and very simple GroupBox control that you might even want to use after the Toolkit includes the GroupBox for sake of the tiny runtime size (only 9 KB).

To use all you need is:

<groupbox:GroupBox Header=”Header goes here”>
    <Button Content=”This is a placeholder for the content”/>
</groupbox:GroupBox>

In case anyone is interested, I did find one other groupbox control out there but it was created before the RTW and requires a little work to get it running on RTW and the spacing around the header was a bit off. Nevertheless, kudos to Lee for putting together something to use way back in March.

Dec 16, 2008: I added source code for C# and fixed a bug allowing the groupbox to be truely transparent by clipping the Border control where the header goes instead of relying on ZOrder to hide that portion of the border control.

Read Full Post »

Try to trap for the focus changed events (GotFocus and LostFocus) in Silverlight in a container control; I dare you! 😉 You’ll quickly realize that all the GotFocus and LostFocus events of all the children controls and their children controls fire on the container. This is of course due to event bubbling (which is a good thing). But, it makes it awefully hard to tell when the container itself is getting focus (after not having it) or loosing focus because something outside your container now has it.

For example: Create a StackPanel (MyContainer) with 2 controls in it and put that next to another control (the button)…

   <StackPanel>
      <StackPanel x:Name=”MyContainer”>
         <TextBox/>
         <TextBox/>
      </StackPanel>
      <Button Content=”button3″/>
   </StackPanel>

Now put debug statements in MyContainer’s GotFocus and LostFocus events…

Private Sub MyContainer_GotFocus(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs) Handles MyContainer.GotFocus
    Diagnostics.Debug.WriteLine("MyContainer_GotFocus")
End Sub
 
Private Sub MyContainer_LostFocus(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs) Handles MyContainer.LostFocus
    Diagnostics.Debug.WriteLine("MyContainer_LostFocus")
End Sub

Hmm, looks like our container lost focus… but it didn’t!? Focus is simply on another child in the same container. The events are firing like this because the children controls are bubbling up their events to the container so it looks like the container is loosing and getting focus each time. So how do we determine if focus has actually been given to a control outside the container or if focus is still inside the container?

The answer is not that hard. all we need to do is determine if at the time the LostFocus event is fired, if the item that has focus is a child of our container. And you can use the System.Windows.Input.FocusManager.GetFocusedElement method to determine which object has focus. I created a nice little IsRelated function to help determine who is a decendent.

Private Shared Function IsRelated(ByVal Child As Object, ByVal Parent As Object) As Boolean
    Return Child Is Parent OrElse (TypeOf Child Is FrameworkElement _
        AndAlso _
        IsRelated(DirectCast(Child, FrameworkElement).Parent, Parent))
End Function
 
Private HasFocus As Boolean
 
Private Sub MyContainer_GotFocus(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs) Handles MyContainer.GotFocus
    If Not HasFocus Then
        HasFocus = True
        Diagnostics.Debug.WriteLine("MyContainer_GotFocus")
    End If
End Sub
 
Private Sub MyContainer_LostFocus(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs) Handles MyContainer.LostFocus
    If Not IsRelated(System.Windows.Input.FocusManager.GetFocusedElement, _
            MyContainer) Then
        HasFocus = False
        Diagnostics.Debug.WriteLine("MyContainer_LostFocus")
    End If
End Sub

And that’s all there is to it. Happy coding!

Read Full Post »

In Silverlight applications practically everything can be animated to provide visually pleasing transitions between changes in your application or web page. When you hide something that is currently visible, why have it just disappear when you can make it fade away or slide off to the edge of the page? Or both? Silverlight makes these kinds of effects so much easier than ever before… so take advantage of it!

Unfortunately, there’s one control that is surprisingly a pain to support this on… the grid control. Try to animate collapsing its rows or columns by animating ColumnDefinition.Width or RowDefinition.Height via:

<DoubleAnimation Storyboard.TargetName=”Column1″ Storyboard.TargetProperty=”Width” To=”0″ Duration=”00:00:00.5″/>

and you’ll quickly discover the following runtime error:

InvalidOperationException: DoubleAnimation cannot be used to animate property Width due to incompatible type.

It turns out, Width is not of type Double but instead is of type GridLength. And GridLength has a property called Value of type Double, but Value is ReadOnly so to change the width or height you have to create a new instance of the GridLength object and set it it to the Width Property. Unfortunately, this is not something that can be done in a DoubleAnimation (that I’m aware of).

The solution: Do it yourself! Add a Name to the root element of your page, animate your own property and have your your property wrap setting the Width property on the ColumnDefinition or RowDefinition.

<DoubleAnimation Storyboard.TargetName=”Myself” Storyboard.TargetProperty=”ColumnWidth” To=”0″ Duration=”00:00:00.5″/>

' Warning: This does not work from an animation
Public Property ColumnWidth() As Double
    Get
        Return Column1.Width.Value
    End Get
    Set(ByVal value As Double)
        Column1.Width = New GridLength(value)
    End Set
End Property
WAIT: Animations can only set DependencyProperties. So the above code will not work. Instead we need to turn this property into a DependencyProperty…

Private Shared ReadOnly ColumnWidthProperty As DependencyProperty = DependencyProperty.Register("ColumnWidth", GetType(Double), GetType(Page), New Windows.PropertyMetadata(AddressOf ColumnWidthChanged))
Private Property ColumnWidth() As Double
    Get
        Return DirectCast(GetValue(ColumnWidthProperty), Double)
    End Get
    Set(ByVal value As Double)
        SetValue(ColumnWidthProperty, value)
    End Set
End Property
Private Shared Sub ColumnWidthChanged(ByVal d As DependencyObject, ByVal e As DependencyPropertyChangedEventArgs)
    DirectCast(d, Page).Column1.Width = New GridLength(DirectCast(e.NewValue, Double))
End Sub


Click To See

Download source code

Read Full Post »

« Newer Posts