Tuesday, 11 August 2009

Determining the size of a Silverlight multi-scale image deep zoom source

Had a bit of a problem today trying to set a multi-scale image with a deep zoom source to fit the height. Fitting the width is easy:

   1: //Reset origin
   2: multiSourceImage.ViewportOrigin = new Point(0, 0);
   3: multiSourceImage.ViewportWidth = 1;

That is right you are just setting the viewport width to 1 which makes sense!! But unfortunately there is no such thing as a viewport height. So another way is required:

   1: //locals
   2: double lastRow = -1;
   3: double lastBottom = 0.0;
   4: double height = 0.0;
   5: bool firstRow = true;
   7: //Loop over images
   8: foreach (MultiScaleSubImage subImage in multiSourceImage.SubImages)
   9: {
  10:     //Have we reached a new row ? (NB this only works with images layed out in a grid)
  11:     if (subImage.ViewportOrigin.Y != lastRow)
  12:     {
  13:         //Have new row
  14:         //Take aspect ratio of image here
  15:         lastRow = subImage.ViewportOrigin.Y;
  17:         //Work out bottom of image - to get to gap
  18:         double width = multiSourceImage.ActualWidth / (multiSourceImage.ViewportWidth * subImage.ViewportWidth);
  19:         double height2 = multiSourceImage.ActualWidth / (multiSourceImage.ViewportWidth * subImage.ViewportWidth * subImage.AspectRatio);
  21:         Point pos = multiSourceImage.LogicalToElementPoint(new Point(
  22:             -subImage.ViewportOrigin.X / subImage.ViewportWidth,
  23:             -subImage.ViewportOrigin.Y / subImage.ViewportWidth)
  24:         );
  25:         Rect rect = new Rect(pos.X, pos.Y, width, height2);
  27:         //Work out gap
  28:         double gapHeight = rect.Top - lastBottom;
  29:         lastBottom = rect.Bottom;
  31:         //height += height2;
  32:         height += (multiSourceImage.ActualWidth / subImage.ViewportWidth) * 1 / (subImage.AspectRatio);
  33:         if (!firstRow)
  34:             height += gapHeight;
  35:         firstRow = false;
  36:     }
  37: }
  39: //Set to actual height here
  40: multiSourceImage.ViewportWidth = height / multiSourceImage.ActualHeight;            
  41: multiSourceImage.ViewportOrigin = new Point(0, 0);

As noted in the notes, this only works if the images are in a nice gridded layout. I guess you can modify it to work with the left hand most image of each row.

It basically works by looping over each sub image. Seeing if that image is on a new line, if it is record its origin y co-ordinate.

For any subsequent rows, work out the gap between the bottom of the image in the row above, and add those dimensions to a running total.

You can work out the height of a sub image simply by

   1: subImageHeight = multiSourceImage.ActualWidth / (subImage.ViewPortWidth * subImage.AspectRatio)

Finally this running total for the height over the size of the multi source image element should give you the viewport width you require.

Setting the viewport origin to 0,0 will also make it line up in the element box nicely.

Monday, 10 August 2009

Silverlight: Accessing child properties after they have been rendered

I came across an interesting issue today. Essentially its to do with the events related to a Silverlight User Control.

The problem I had was related to a child control which I wanted to set in its parent after the control had been rendered. For example setting a width on a child canvas control. The issue I had was when I wanted to set the parameter it had not yet been created, as loaded event is fired on the parent before the child.

The initialisation cycle of a Silverlight control (source Control Lifecycle), states the last event to be called is the ‘LayoutUpdated’ event. This seems to be fired when the child control has finished being rendered. Interesting!

   1: private void UserControl_LayoutUpdated(object sender, EventArgs e)
   2: {
   3:     //All controls are hopefully loaded set the template
   4:     ChildControl.ChildProperty.Width = <ChildValue>;
   5: }