Welcome to MSDN Blogs Sign in | Join | Help

As I was digging around in an old spec, I came across some very enlightening drawings which I pushed through art review and I will now share with you.  Ashish wrote a nice post on the layout and rendering engines which might help you understand what's happening when you put something in a panel.  This is particularly useful if you are trying to do a complex layout.  Also, as a side note, I really recommend putting background colors on your element or using borders when you are first trying to get something to layout properly.  It can really help you figure out what is going on. 

As I was going through my list of APIs to document, I stumbled onto the LayoutInformation class.  In it are three methods, two of which I found especially interesting (GetLayoutSlot and GetLayoutClip).  The other one (GetLayoutExceptionElement) I'm sure would be interesting if you were getting an exception during layout.  But happily I have not run across that issue.

As I discussed in my previous post Creating a Custom Panel, a lot of the layout process is really about positioning the bounding box (or layout slot) that surrounds each element.  If you have trouble visualizing this, you are going to love these pictures.  GetLayoutSlot returns the rectangle that is your bounding box.  The element that is inside the layout slot can be bigger or smaller.  If its smaller, then it is positioned inside the layout slot based on it's alignment properties like so:

Layout Slot

If the element is bigger than its layout slot, then it will be clipped and only the area which is inside of the layout slot will be displayed. 

Layout Clip

This visible region (outlined in red) is what GetLayoutClip returns to you.  The size of the layout slot can change as elements are added or removed from the parent container.  Basically every time the layout engine is called, there is a chance for the layout slot to change.

 Here's some code that calls GetLayoutSlot and draws the returned rectangle.  First the XAML:

<Grid x:Name="LayoutRoot" Background="White">

    <Grid.RowDefinitions>

        <RowDefinition/>

        <RowDefinition/>

    </Grid.RowDefinitions>

    <StackPanel x:Name="sp1" Grid.Row="0" Background="AliceBlue" Height="100" Width="200"

                HorizontalAlignment="Left" VerticalAlignment="Top" >

         <Rectangle x:Name="rect1" Fill="Blue"  Width="100" Height="50" ></Rectangle>

    </StackPanel>

    <Button Content="Get Layout Slot" Grid.Row="1" Width="150" Height="50"

            Click="Button_Click" HorizontalAlignment="Left"/>

</Grid>

 

And here's the click event handler:

 

 

private void Button_Click(object sender, RoutedEventArgs e)

{

   //Get Layout Slot of Rectangle

   Rect r1 = LayoutInformation.GetLayoutSlot(rect1);

   RectangleGeometry rg1 = new RectangleGeometry();

   rg1.Rect = r1;

   Path mypath = new Path();

   mypath.Data = rg1;

   mypath.Stroke = new SolidColorBrush(Colors.Black);

   mypath.StrokeThickness = 5;

   LayoutRoot.Children.Add(mypath);

 

   //Get Layout Slot of StackPanel

   Rect r2 = LayoutInformation.GetLayoutSlot(sp1);

   RectangleGeometry rg2 = new RectangleGeometry();

   rg2.Rect = r2;

   Path spSlotPath = new Path();

   spSlotPath.Data = rg2;

   spSlotPath.Stroke = new SolidColorBrush(Colors.Red);

   spSlotPath.StrokeThickness = 3;

   LayoutRoot.Children.Add(spSlotPath);

}

 

And here's what it looks like after you run it and click the button:

 

GetLayoutSlot sample screenshot

 

You can see the Rectangle in dark blue and it's layout slot is outlined in black.  The StackPanel is in light blue.  I highlighted it's layout slot in red so that you could see that even though the StackPanel only requested 200x100 in area, the parent Grid actually gave it a much larger slot. 

 

Margaret

 

 

 

 

Deep Zoom which is a technology that combines Silverlight with SeaDragon is a powerful and easy way to impliment zooming and panning large images. Below are a few examples.

  • Exploration of Very Large or High Resolution Images with Great Performance: Take a huge high resolution image or collection of images (e.g. a map or collection of high-res pics) and allow users to zoom WAY in and pan around the image(s). If you haven't checked out the Hard Rock Cafe Memorabilia site have a look. You can zoom with the mousewheel and pan around by dragging. As the user moves their view, animations are used to give the user the impression that they are "moving" from one place to the other over the images.
  • 3-D Photography: Take pictures of a room, one after the other to make a collection of photos that make up a 360 degree picture of the room. Now the user can pan around the room with each photo blending into the other. I haven't tried this yet but I saw a demo somewhere and it seems like it would be relatively easy. Make a nice real-estate app and they can use all the help they can get at the moment.
  • Advertisements: You could create a relatively low resolution image to represent the overall theme of the ad, and then have progressively higher resolution images containing more impressions and data about the product. When the page the ad is embedded in first loads, the ad smoothly sharpens and draws the attention of the reader by loading subsequently higher resolution images. In addition, if the user's mouse enters the ad, different parts of the ad can zoom in.

So how to get started developing with Deep Zoom? Well, I'm not going to re-hash what others have already done a great job on. Below is a list of resources. In a few hours you'll have your own cool DZ app up and running:

  • Deep Zoom Sample For those who just want to dive straight into code. This app allows you to do all the basic stuff like mousewheel scrolling, and panning around by dragging. This app also provides zoom buttons for users who don't have a scrollwheel or their browser doesn't support them. Compliments to Lutz Gerhard for this. One note: When it comes time for you to use your own source images,  you have to compile them into a special format. There is a free (as of writing this) slick tool for doing this called Deep Zoom Composer.
  • Deep Zoom Primer (Blog Post): Introduction to Deep Zoom along with step by step code with explanations. Really noise!
  • Filter Example (Blog Post): Another useful post including a sample where the images fit within a container and the individual images can be filtered.

Sam

I've recently taken over the documentation for some of the Silverlight controls, and one of the controls I now own is Popup. One of the first things I do when preparing to document a control is play around with it in code. Using the Popup control is pretty straightforward, but I wanted to share the basics, particularly since we didn't get the documentation in for Beta1 (see Margaret's post; we are a bit understaffed) Popup is useful for hosting content, particularly a UserControl, in a new "popup" on top of the existing Silverlight content.

To get started, I created a Silverlight application project and added a user control named MyControl to the project.

Following is the code for my user control. It's a control that contains a button which, when clicked, causes the pop-up to disappear. (It's basic--I don't want to detract from the main point of this post). First the code in MyControl.xaml:

<UserControl x:Class="PopupEx.MyControl"
    xmlns="
http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Width="150" Height="80">
    <Border BorderThickness="5" BorderBrush="Black">
    <StackPanel x:Name="LayoutRoot" Background="White">
       <TextBlock Margin="5" Text="See the popup?" />
        <Button Width=" 50" Content="Yes" Click="Button_Click"/>
    </StackPanel>
    </Border>
</UserControl>

Here's the click event handler for MyControl.xaml.cs. The event-handler simply sets the Visibility property to Visibility.Collapsed, to remove it from the layout.

private void Button_Click(object sender, RoutedEventArgs e)
{
       // Remove the control from the layout.
       this.Visibility = Visibility.Collapsed;
}

Next I'll add a button to Page.xaml for my main Silverlight control, which when clicked, shows my user control. Paste this into the existing Grid in Page.xaml:

<Button Width="100" Height="50" x:Name="showPopup" Click="showPopup_Click"
        Content="Show Popup" />

My next task is to handle the click event for the ShowPopup button. I'll need to add a using statement for System.Windows.Controls.Primitives to Page.xaml.cs, because Popup is in the Primitives namespace:

using System.Windows.Controls.Primitives;

Now I need to handle the Click event and show my user control. I'll create a new Popup object and set its Child property to my user control. I can optionally tweak where the popup shows up on the screen with VerticalOffset and HorizontalOffset. Finally, I'll show the popup by setting its IsOpen property to true.

// Declare the popup object.
Popup p;
private void showPopup_Click(object sender, RoutedEventArgs e)
{
    // Create a popup.
    p = new Popup();

    // Set the Child property of Popup to an instance of MyControl.
    p.Child = new MyControl();

    // Set where the popup will show up on the screen.
    p.VerticalOffset = 200;
    p.HorizontalOffset = 200;

    // Open the popup.
    p.IsOpen = true;
}

That's all there is to it. When you run the sample, you can click the button to show the popup. When you click the popup button, the popup disappears.

--Cheryl

  

Ever wonder how the MSDN documentation gets written?  Who are these people?  What do they do?  Where do they come from?  Here are the answers to a few of those questions as I blog about a random day as a writer on the Silverlight User Education team. 

 

7am

Leave for work in my car.  Yes, I’m one of those crazy early morning people but only because I have kids now.  Before kids, I would roll in around 10am and you don’t even know how much I miss those days.  Some days I carpool with my manager, Dawn.  Soon, I will be walking a few blocks to catch the Connector (yay!) where I can work and commute in style.

 

7:30am

I arrive bright and early before almost everyone except Cheryl who gets in sometime before the crack of dawn.  After doing a brief scan of my email to see if anything exciting has happened in the last 12 hours, Cheryl and I (and sometimes Dawn) wander down to the cafeteria for a morning coffee/snack.  Then back into our offices and the real work begins.

 

8am

Check my tasks in outlook.  This is where I personally keep a lot of my notes about what I'm working on, things I want to investigate more, things I need to follow-up on, etc.  Usually a quick scan will inspire me to get started.  Today, I'm working on a new topic for the Silverlight docs.  I want it to be somewhere between a brief overview of all the top features and a tutorial.  One stop shopping!  We'll see how it turns out.  Everyone's writing methodology is different.  I tend to have a general idea and a rough outline and then I do stream of consciousness until I'm dry.  Then I go back and see if I made any sense.  Sometimes I have, which is great.  It means I can clean it up, add some code, art, and links and toss it out for tech review and editing.  Other times, it’s a mess and I have to figure out what to do with it.

 

10am

Tired of writing.  Sam, my officemate shows up.  He's supposed to be working from home today but is having connectivity problems which we discuss before he sticks his headphones on.

 

10:15am

Need snack and some water.  Head to the kitchen.  Return to desk.

 

10:20am

Can't face writing anymore this morning.  Time for some code.  As I was writing I had a general idea of the code samples I wanted to put into the doc.  Now, I need to write them and see if the whole thing holds together.  I usually start with a code sample before I write, but this is an unsual topic and I wasn't sure what I wanted to show until I wrote some ideas down.

 

11:20am

Coding turned out to be super easy, so far.  I was just doing basics like creating a control, adding an event handler and changing the color of a shape.  Now I must "snippet" it.  This involves adding tags that will allow the build to strip out the relevant bits of code and stick it into my document.

 

11:45am

Yet!  Its lunch time.  My tummy is rumbling as my son would say.  Luckily today is a fun day and I'm heading off campus for lunch with an old coworker. 

 

1pm

Ok, I'm back, I'm full, I'm sleepy, and I have a meeting to go to.  Bad, bad combination.  Luckily, this is more of the 1 on 1 type meeting which is not as sleep inducing as say an all hands (large team meetings involving uncomfortable chairs, bad power point decks, and free food!).

 

1:30pm

Doc scrum.  Meet in Wolf's office for roundtable.  This generally gets out of hand and we end up discussing some random issue that is interesting/annoying/challenging.  Today's topic ends up being the controls overviews.  We've never managed to keep this meeting to the 15 min it’s scheduled for.  Still, the topics from this meeting generally end up being something I'm glad that I know about later.

 

2:00pm

Done with meetings.  Back to the real work.  Still don’t want to go back to my new document from this morning, so I decide to follow-up on some issues.  I need to talk to a guy about what his team thinks about quickstarts.  Send random PM that I don't know an email.  Wait to hear back.

 

2:10pm

Need to create new art for some documents.  Go into the tool, click on New Art.  Arg!  Something is broken in my tool and I cannot create new art.  Send email to tools team.  No one seems to know what is going on.  Contemplate re-installing tools but really don't want to.  Must get Sam to make art request for me when he returns from meeting land.

 

2:30pm

Want to write code - time to find something new to work on.  Let's see, what to pick from – will I play with binding to collections, the converter parameter, ooh… How about Grid?!  Ok, Grid it is.  I still feel perplexed by grid.  Let's see if I can find the spec.  Ahh, here it is.  Now I must come up with an interesting yet fairly simple sample idea.  Think, think, think.  Ok, have idea, start writing code.

 

This is probably, for me, the most fun part of the job.  Basically we get to research what the features actually do as opposed to what the spec claims.  Luckily the Silverlight team has been generally great about producing features that are pretty close to how they were spec'd out.  Still, specs rarely contain every little corner case and details of what happens when you are interacting with other features.  So, it’s a little like research to feel that you have a good handle on any given topic.  And since time is always a factor, you can't spend too long playing with something before you sit down and start writing. 

 

3:30pm

I feel like I have a decent handle on some of the APIs, so I open up the API pages and start adding some content.

 

3:45pm

"Joe" PM stops by to see how the UE world is going.  I ask about the new features.  Are they all still going in?  Yes, in fact they are almost all checked in.  Make mental note to install a new build soon.  Ask "Joe" about documentation priorities.  Make note of his opinion.  Luckily, it matched up well with mine so I don't have to overhaul my doc plans.

 

4:10pm

I'd better get going if I want to beat the traffic!

 

This is a fairly typical day for a Programming Writer.  Sometimes we have a few more meetings; sometimes we have side projects related to doc builds, posting content on community sites, writing whitepapers for MSDN, thinking about content architecture, understanding search engines and discoverability, etc.  If it’s related to helping customers use our products and our documentation, we spend time thinking about it. 

 

Writers come from a variety of backgrounds.  I was pursuing a PhD in Geophysics before I joined MSFT.  But the people on our team have been developers, testers, PMs, scientists, writers, journalists and editors.  If you like to write code, you want to help customers use great products, you love playing with the latest and greatest technology, and you like writing, you should think about being a Programming Writer.  Specifically, if you think that sounds like fun and you think Silverlight is the best thing since sliced bread, then apply to our open position! 

 

-Margaret

 

I've spent time the last couple weeks trying to figure out how to create my own panel.  At first I was completely confused and it seemed like I would change something small and the entire layout of my panel would radically shift.  But after stumbling around in the dark for a while, I feel like the light has clicked on, so I'll see if I can give some advice on custom panels.

 

First you need to know about Measuring and Arranging.  The Silverlight layout system starts by doing a Measure pass.  This is when each child element tells the layout system its desired size.  This includes the panel itself.  Then there is an Arrange pass.  Here the final size and position of each child's bounding box is specified as well as the final size of the panel itself.

 

So, to create your own panel, you need to create a class that derives from Panel and implements the MeasureOverride and ArrangeOverride methods.  I've created a panel that I'm calling BlockPanel.  It creates 3x3 blocks of elements and then lays each block side-by-side wrapping them around when it reaches the right edge of the panel.  I've limited each element to be 100x100.  I was going to do something more complicated where I figured out what size the element wanted to be and then adjusted my blocks but the code was getting too long and complex for an SDK sample.  So, here is a picture of how the children will get layed out by my panel:

 

 

Now let's go through the code.  Here is the MeasureOverride method for my panel:

 

public class BlockPanel : Panel

    {

        //First measure all children and return available size of panel

        protected override Size MeasureOverride(Size availableSize)

        {

            //Measure each child

            foreach (FrameworkElement child in Children)

            {

                child.Measure(new Size(100,100));

            }

 

            //return the available size

            return availableSize;

        }

 

 

}

 

What happens is that the layout system passes you the size available to your panel.  If you have specifically set the height and width of the panel when you created it, that is what you get.  Inside MeasureOverride, I'm calling Measure on each child passing the size that my panel is making available, in my case 100x100.  In a lot of the samples I've seen, availableSize is passed back, which basically says that the child can take up the entire area of the panel.  After Measure is called, the layout system will determine the desired size of the child.  I haven't figured out all the details of this, but it seems like for the most part it takes the smaller of the available size and the native size.  So, in my BlockPanel, if you have a child Rectangle that is 200x200, the desired size would be set to 100x100.  If the Rectangle was 50x50, then the desired size would be 50x50.  

 

After I measure all the children, then I return the size available to the whole panel.  I'm returning the availableSize that was passed in, so I'm not restricting the size of the panel in Measure at all.

 

Next the layout system will look for ArrangeOverride.  Here is the ArrangeOverride for my Panel:

public class BlockPanel : Panel

    {

        //Second arrange all children and return final size of panel

        protected override Size ArrangeOverride(Size finalSize)

        {

            //Get the collection of children

            UIElementCollection mychildren = Children;

 

            //Get the total number of children

            int total = mychildren.Count;

 

            //Calculate the number of 3x3 blocks needed

            int blocks = (int)Math.Ceiling((double)total/9.00);

 

            //Calculate how many 3x3 blocks fit on a row

            int blocksInRow = (int)Math.Floor(finalSize.Width / 300); //assuming blocks of 9 element 300x300

 

            //Arrange children

            int i;

           

            double maxWidth = 0;

            double maxHeight = 0;

            for (i = 0; i < total; i++)

            {

                //Find out which 3x3 block you are in

                int block = FindBlock(i);

 

                //Get (left, top) origin point for your 3x3 block

                Point blockOrigin = GetOrigin(block, blocksInRow, new Size(300,300));

 

                //Get (left, top) origin point for the element inside its 3x3 block

                int numInBlock = i-9*block;

                Point cellOrigin = GetOrigin(numInBlock, 3, new Size(100,100));

 

                //Arrange child

                //Get desired height and width. This will not be larger than 100x100 as set in MeasureOverride.

                double dw = mychildren[i].DesiredSize.Width;

                double dh = mychildren[i].DesiredSize.Height;

                

                mychildren[i].Arrange(new Rect(blockOrigin.X + cellOrigin.X, blockOrigin.Y + cellOrigin.Y, dw, dh));

 

                //Determine the maximum width and height needed for the panel

                maxWidth = Math.Max(blockOrigin.X + 300, maxWidth);

                maxHeight = Math.Max(blockOrigin.Y + 300, maxHeight);

            }

 

 

            //Return final size of the panel

            return new Size(maxWidth,maxHeight);

        }

}

 

Basically what I'm doing is calculating for each element:

1.      which 3x3 block its in

2.      which cell number in that 3x3 block its in

3.      the location of the Left, Top corner of the 3x3 block

4.      the location of the Left, Top corner of the cell inside the 3x3 block

5.      the desired height and width of that element

 

Once I have all these pieces of information, then I can compute location and size of the bounding box for each child element.  That bounding box is the Rect that I pass to Arrange. 

 

I also keep track of the width and height needed to contain all the 3x3 blocks.  Then I set the final size of the panel to be just big enough to contain all the blocks.  I could also return finalSize and then my panel would take up the entire area available to it from its parent container.

 

So, a couple of points that had me confused for a while:

  1. When you call child.Measure, you are passing in the size that is available to that child.  You are not actually setting the size of the child.
  2. After you call Measure, the layout system will determine the desired size (desiredSize) of the element.  As far as I can tell, there is no way for you to set the desired size yourself.
  3. When you call child.Arrange, you are not setting the final size of the child.  I thought for a while that this would set the size of the child, but it sets the bounding box that will contain the child.  So, if the child is bigger than the bounding box, it gets clipped.  If the child is smaller, it is positioned in the bounding box based on the default behavior or whatever alignment you have specified.  Note that in my case, I set the bounding box to be the desired size of the child.

I've attached the rest of the code which includes the methods I'm calling from ArrangeOverride and a XAML file that creates my panel and puts some rectangles in it.  There are some details of the layout system that I haven't really gotten into in this example, so I recommend reading Object Positioning and Layout if you haven't already.  Also, you will need to map your namespace to create your custom panel in XAML, so check out Mapping to Custom Classes and Assemblies.

 

Margaret

The WebClient and HttpWebRequest classes can both be used for retrieving data from a Web service. WebClient is most useful for one-time retrieval of data from a Web service, or downloading remote resources (for more information on this see, "Downloading Content on Demand") and I think, is somewhat easier to use. HttpWebRequest is perhaps a bit less user-friendly, but will enable you to set headers and generally offers more control over a Web service request.

 

The following code demonstrates how to make the same request using both classes. I’ve set up a simple Silverlight control that contains two buttons, a text box to capture user input, and a text block to display the content returned from the Web service. The first example demonstrates how to make a request using WebClient, the second demonstrates the same request using HttpWebRequest. Both requests are asynchronous.

 

First, I show you the simple UI. It’s some stack panels, two buttons, a text box that accepts user and a text block to display the output from the Web service.

<StackPanel Background="White">

    <StackPanel Orientation="Horizontal">

        <Button Width="250" Content="Click for request with

          HttpWebRequest" Click="Button1_Click"  />

         <Button Width="250" Content="Click for request with WebClient"

            Click="Button2_Click" />

    </StackPanel>

    <StackPanel Orientation="Horizontal">

        <TextBlock  Text="Enter a number between 1-5: " />

        <TextBox  Text="1" x:Name="numTextBox" Width="20" />

    </StackPanel>

    <TextBlock x:Name="resultBlock" />

</StackPanel>

The request will go to a Digg Web service that returns the last photos uploaded to its gallery service. This service that allows you to use the URL to specify the expected response type and the number of photos to return, which I will do. In addition, I must specify an AppKey, which is required by the Digg Web service. It’s important to notice that Digg has the required cross domain policy in place to allow calls from my Silverlight application.

 

First I declare the base URL used by both WebClient and HttpWebRequest:

string baseUri = http://services.digg.com/galleryphotos?count;

 

I’ll use the same URL with both WebClient and HttpWebRequest, but since I'll append the number of photos to return, I’ll build the complete URL for each request using the specified number and the AppKey. In addition, I'll specify an xml response by appending &type=xml to the end of the URL.

 

To call the service by using WebClient, declare a WebClient object, and in the Page constructor associate its DownloadStringCompleted event with a handler.

 

WebClient client = new WebClient();

 

client.DownloadStringCompleted +=

    new DownloadStringCompletedEventHandler(

    client_DownloadStringCompleted);

 

In Button1_Click, I call DownloadStringAsync, passing the complete URL that includes the number specified by the user.