Hi Folks,
I’d like for us to take some time and talk about the new Silverlight Toolkit Viewbox control.
We’ll deep dive on what Viewbox does, how it does it and the few nooks and crannies it has.
Setup
1. Create a new project.
2. Add a reference to the Silverlight Controls assembly (Microsoft.Windows.Controls.dll) which can be downloaded at http://codeplex.com/Silverlight.
3. Look under "Custom Controls" In the Blend Asset Library.
4. Add a ViewBox to the Page.
And here's the XAML we got:
<UserControl
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
x:Class="SilverlightControlsNovember2008.ViewBoxPage"
Width="640" Height="480" xmlns:controls="clr-namespace:Microsoft.Windows.Controls;assembly=Microsoft.Windows.Controls" xmlns:SilverlightControlsNovember2008="clr-namespace:SilverlightControlsNovember2008">
<Grid x:Name="LayoutRoot" Background="#FFFFFFFF">
<controls:Viewbox Height="100" HorizontalAlignment="Left" VerticalAlignment="Top" Width="100"/>
</Grid>
</UserControl>
Introducing Dobby The Penguin
ViewBox is meant to resize SIlverlight XAML to fit the available screen realestate.
As such, it’s optimal to be used in 2D graphics and less relevant for ordinary controls.
We’ll demo ViewBox with some 2D content. However, my drawing talent is limited to that of a 3yr old.
So, with the gracious approval of Jose Fajardo we’ll be using one of his XAML drawings – Dobby the penguin.
Dobby enjoys snow-boarding, long walks on the beach, and is 320x300 pixels big. (approx. Width x Height)
Dobby has a pretty complex XAML file (which you can find here). In order to reuse Dobby without copy-pasting his XAML all over the place, we’ll create Custom Penguin control.
I’ve pasted the XAML into Blend, and now we’ll “right click –> Make Control” on the penguin canvas.
We’ll call our now control “PenguinControl”.
And here’s the XAML behind our Penguin Control:(you can copy the full XAML from here)
<UserControl
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
x:Class="SilverlightControlsNovember2008.PenguinControl" Width="323" Height="301"
>
<Grid x:Name="LayoutRoot">
<Canvas Height="301" x:Name="Penguin" Margin="0,0,317,179" Width="323">
… <!-- Full Xaml Here -->
</Canvas>
</Grid>
</UserControl>
ViewBox with Fill Stretch
We’ve mentioned that ViewBox resizes it’s content based on it’s own size. In order to test that theory we’ll create 3 ViewBoxes:
1. The First (100,100) pixels
2. The second (250,250) pixels.
3. The Third (400,400) pixels.
We’ll also set our Viewboxes to use Stretch to Fill.
Next, we’ll put Each ViewBox in a Border that has a BorderBrush off Black and a BorderThickness of 1.
We’ll click on BorderBrush and than “Solid Color Brush”.
-->
And we’ll set BorderThickness to 1.
And here’s our ViewBoxes
Here’s the XAML blend generated for us up until now:
<UserControl
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
x:Class="SilverlightControlsNovember2008.ViewBoxPage"
Width="800" Height="480" xmlns:controls="clr-namespace:Microsoft.Windows.Controls;assembly=Microsoft.Windows.Controls" xmlns:SilverlightControlsNovember2008="clr-namespace:SilverlightControlsNovember2008">
<Grid x:Name="LayoutRoot" Background="#FFFFFFFF">
<Border BorderBrush="#FF000000" BorderThickness="1,1,1,1" HorizontalAlignment="Left" VerticalAlignment="Top">
<controls:Viewbox Height="100" Width="100" Stretch="Fill" />
</Border>
<Border BorderBrush="#FF000000" BorderThickness="1,1,1,1" HorizontalAlignment="Left" Margin="120,0,0,0" VerticalAlignment="Top" >
<controls:Viewbox Height="200" Width="200" Stretch="Fill" />
</Border>
<Border BorderBrush="#FF000000" BorderThickness="1,1,1,1" Margin="340,0,60,80">
<controls:Viewbox Height="400" Width="400"Stretch="Fill" />
</Border>
</Grid>
</UserControl>
Now let’s place Dobby the Penguin into each of our Viewboxes.
And we can see Dobby has been resized to Fill the ViewBox.
Stretch.Fill tells the ViewBox –" “make sure this content takes up the entire width & height of the Viewbox”.
ViewBox with Uniform Stretch
Fill Stretch makes sure the element fills up both Height & Width.
The only way to scale up an item to fill those both is to distort the ratio between Height & Width.
As you can see in this exciting table, Dobby’s fill ratio is equal to that of the viewbox – 1.
However, Dobby’s original ration was 1.07. That means that making Dobby scale to fill distorts him.
If we set the Viewbox Stretch to Uniform we’ll get the same ratio though.
And this is how Dobby likes like now:
Let me crank up mspaint and highlight the new whitespaces in our Viewboxes.
We can see that Dobby is now filling all the Width of all the Viewboxes, but not all height.
The reason is that the Viewbox is smart enough how to scale up/down the controls in it without distorting them or changing their ratio.
Here’s the XAML Blend generated for us:
<Border BorderBrush="#FF000000" BorderThickness="1,1,1,1" HorizontalAlignment="Left" VerticalAlignment="Top">
<controls:Viewbox Height="100" Width="100" Stretch="Uniform">
<SilverlightControlsNovember2008:PenguinControl/>
</controls:Viewbox>
</Border>
<Border BorderBrush="#FF000000" BorderThickness="1,1,1,1" HorizontalAlignment="Left" Margin="120,0,0,0" VerticalAlignment="Top" >
<controls:Viewbox Height="200" Width="200" BorderBrush="#FF000000" BorderThickness="1,1,1,1" Stretch="Uniform">
<SilverlightControlsNovember2008:PenguinControl/>
</controls:Viewbox>
</Border>
<Border BorderBrush="#FF000000" BorderThickness="1,1,1,1" Margin="340,0,60,80">
<controls:Viewbox Height="400" Width="400" BorderBrush="#FF000000" BorderThickness="1,1,1,1" Stretch="Uniform">
<SilverlightControlsNovember2008:PenguinControl/>
</controls:Viewbox>
</Border>
ViewBox with Uniform Stretch, HortizontalAlignment and VerticalAlignment
As we’ve seen a Viewbox with Stretch to Uniform might have some Whitespace left on the Top & Bottom or the Left & Right.
By default, Viewbox places the control in HorizontalAlignment and VerticalAlignment of Center.
We’ll select our Uniform Viewboxes and set their VerticalAlignment to Top.
Let’s see how Dobby looks like now:
We can see Dobby still has the same amount of Vertical Whitespace, But now Dobby is aligned to the Top and the Whitespace is all at the Bottom.
Here’s the XAML we created here:
<Border BorderBrush="#FF000000" BorderThickness="1,1,1,1" HorizontalAlignment="Left" VerticalAlignment="Top">
<controls:Viewbox Height="100" Width="100" Stretch="Uniform" VerticalAlignment="Top">
<SilverlightControlsNovember2008:PenguinControl/>
</controls:Viewbox>
</Border>
<Border BorderBrush="#FF000000" BorderThickness="1,1,1,1" HorizontalAlignment="Left" Margin="120,0,0,0" VerticalAlignment="Top" >
<controls:Viewbox Height="200" Width="200" BorderBrush="#FF000000" BorderThickness="1,1,1,1" Stretch="Uniform" VerticalAlignment="Top">
<SilverlightControlsNovember2008:PenguinControl/>
</controls:Viewbox>
</Border>
<Border BorderBrush="#FF000000" BorderThickness="1,1,1,1" Margin="340,0,60,80">
<controls:Viewbox Height="400" Width="400" BorderBrush="#FF000000" BorderThickness="1,1,1,1" Stretch="Uniform" VerticalAlignment="Top">
<SilverlightControlsNovember2008:PenguinControl/>
</controls:Viewbox>
</Border>
Similarly if we set VerticalAlignment to Bottom, the opposite of what we’ve just seen would be true.
Here’s how Dobby looks like now:
And we can see that Dobby is aligned to the Bottom and the Whitespace is on the Top.
Here’s the XAML blend generated for us:
<Border BorderBrush="#FF000000" BorderThickness="1,1,1,1" HorizontalAlignment="Left" VerticalAlignment="Top">
<controls:Viewbox Height="100" Width="100" Stretch="Uniform" VerticalAlignment="Bottom">
<SilverlightControlsNovember2008:PenguinControl/>
</controls:Viewbox>
</Border>
<Border BorderBrush="#FF000000" BorderThickness="1,1,1,1" HorizontalAlignment="Left" Margin="120,0,0,0" VerticalAlignment="Top" >
<controls:Viewbox Height="200" Width="200" BorderBrush="#FF000000" BorderThickness="1,1,1,1" Stretch="Uniform" VerticalAlignment="Bottom">
<SilverlightControlsNovember2008:PenguinControl/>
</controls:Viewbox>
</Border>
<Border BorderBrush="#FF000000" BorderThickness="1,1,1,1" Margin="340,0,60,80">
<controls:Viewbox Height="400" Width="400" BorderBrush="#FF000000" BorderThickness="1,1,1,1" Stretch="Uniform" VerticalAlignment="Bottom">
<SilverlightControlsNovember2008:PenguinControl/>
</controls:Viewbox>
</Border>
In a very similar fashion, we could have set HorizontalAlignment for placement of Horizontal whitespace.
ViewBox with UniformToFill Stretch
UniformToFill is very interesting in that it Fills the Viewbox, but in the same time doesn’t change the Height-Width ration of the Control nested in it.
Let’s see that.
Here’s our design-surface looks like:
We can see that Dobby fills all the space given to it and yet keeps his original Height-Width ratio.
However, Dobby is clipped on the right side to allow that. Similarly Dobby might have been clipped on the bottom if it was necessary.
Here’s the XAML Blend generated for us:
<Border BorderBrush="#FF000000" BorderThickness="1,1,1,1" HorizontalAlignment="Left" VerticalAlignment="Top">
<controls:Viewbox Height="100" Width="100" Stretch="UniformToFill" VerticalAlignment="Bottom" HorizontalAlignment="Stretch">
<SilverlightControlsNovember2008:PenguinControl/>
</controls:Viewbox>
</Border>
<Border BorderBrush="#FF000000" BorderThickness="1,1,1,1" HorizontalAlignment="Left" Margin="120,0,0,0" VerticalAlignment="Top" >
<controls:Viewbox Height="200" Width="200" BorderBrush="#FF000000" BorderThickness="1,1,1,1" Stretch="UniformToFill" VerticalAlignment="Bottom">
<SilverlightControlsNovember2008:PenguinControl/>
</controls:Viewbox>
</Border>
<Border BorderBrush="#FF000000" BorderThickness="1,1,1,1" Margin="340,0,60,80">
<controls:Viewbox Height="400" Width="400" BorderBrush="#FF000000" BorderThickness="1,1,1,1" Stretch="UniformToFill" VerticalAlignment="Bottom">
<SilverlightControlsNovember2008:PenguinControl/>
</controls:Viewbox>
</Border>
ViewBox with No Stretch
One more option of ViewBox is to just Clip to original content without resizing it.
Let’s have a look at that.
Here’s how page looks like:
We can see see in the first & second Viewbox that Dobby is clipped based on his top-right corner.
The third Viewbox shows Dobby aligned to the Center in it’s original size.
Here’s the XAML Blend generated for us:
<Border BorderBrush="#FF000000" BorderThickness="1,1,1,1" HorizontalAlignment="Left" VerticalAlignment="Top">
<controls:Viewbox Height="100" Width="100" Stretch="None" HorizontalAlignment="Stretch">
<SilverlightControlsNovember2008:PenguinControl/>
</controls:Viewbox>
</Border>
<Border BorderBrush="#FF000000" BorderThickness="1,1,1,1" HorizontalAlignment="Left" Margin="120,0,0,0" VerticalAlignment="Top" >
<controls:Viewbox Height="200" Width="200" BorderBrush="#FF000000" BorderThickness="1,1,1,1" Stretch="None">
<SilverlightControlsNovember2008:PenguinControl/>
</controls:Viewbox>
</Border>
<Border BorderBrush="#FF000000" BorderThickness="1,1,1,1" Margin="340,0,60,80">
<controls:Viewbox Height="400" Width="400" BorderBrush="#FF000000" BorderThickness="1,1,1,1" Stretch="None">
<SilverlightControlsNovember2008:PenguinControl/>
</controls:Viewbox>
</Border>
ViewBox with StretchDirection Up & Down
We might sometime want to limit the scaling Viewbox does to only enlarge or shrink the controls in it.
Setting the StretchDirection to UpOnly says the Viewbox can only enlarge controls in it.
Setting the StretchDirection to DownOnly says the Viewbox can only shrink controls in it.
By default StretchDirection is set to Both which states that both Up & Down are acceptable.
Let’s examine how Dobby looks like in each of the 3 Stretch options in each of the StretchDirections.
Stretch=Fill, StretchDirection=Both
we’ve seen this already, this is standard Fill.
Stretch=Fill, StretchDirection=UpOnly
Viewbox #1 & Viewbox #2 have not been resized, because they should have scaled down, but we’re only allowed to scale up.
So Viewbox #1 & Viewbox #2 are equivalent to Stretch=None.
Viewbox #3 is still sized to Fill, because it’s been scaled up and that’s allowed.
Stretch=Fill, StretchDirection=DownOnly
Viewbox #1 & Viewbox #2 that need to be scale down their content have done so.
Viewbox #3 that needs to scale up has not done so, and is now equivalent to Stretch=None.
Stretch=Uniform, StretchDirection=Both <—Default for viewbox
Normal Uniform fill that we’ve previously seen.
Stretch=Uniform, StretchDirection=UpOnly
Viewbox #1 & Viewbox #2 that needed to be scaled down, have not done so because it’s not allowed. So they’re equivalent to having Stretch=None.
Viewbox #3 has been stretched to uniform with scaling up as expected.
Stretch=Uniform, StretchDirection=DownOnly
Viewbox #1 & Viewbox #2 that need to scale down in a Uniform Stretch have done so.
Viewbox #3 that needs to scale up has not done so, and is not equivalent of Stretch=None.
Stretch=UniformToFill, StretchDirection=Both
Normal UniformToFill stretching.
Stretch=UniformToFill, StretchDirection=DownOnly
Viewbox #1 & Viewbox #2 that needs to scale down in a UniformToFill Stretch have done so.
Viewbox #3 that needs to scale up has not done so, and is not equivalent of Stretch=None.
Stretch=UniformToFill, StretchDirection=UpOnly
Viewbox #1 & Viewbox #2 that needed to be scaled down have not done so because it’s not allowed.
So Viewbox #1 & Viewbox #2 equivalent to having Stretch=None.
Viewbox #3 has been stretched to uniform with scaling up as expected.
And here’s the XAML Blend generated for us for Stretch=Uniform & StretchDirection=Both: (the defaults)
<Border BorderBrush="#FF000000" BorderThickness="1,1,1,1" HorizontalAlignment="Left" VerticalAlignment="Top">
<controls:Viewbox Height="100" Width="100" Stretch="Uniform" HorizontalAlignment="Stretch" StretchDirection="Both">
<SilverlightControlsNovember2008:PenguinControl/>
</controls:Viewbox>
</Border>
<Border BorderBrush="#FF000000" BorderThickness="1,1,1,1" HorizontalAlignment="Left" Margin="120,0,0,0" VerticalAlignment="Top" >
<controls:Viewbox Height="200" Width="200" BorderBrush="#FF000000" BorderThickness="1,1,1,1" Stretch="Uniform" StretchDirection="Both">
<SilverlightControlsNovember2008:PenguinControl/>
</controls:Viewbox>
</Border>
<Border BorderBrush="#FF000000" BorderThickness="1,1,1,1" Margin="340,0,60,80">
<controls:Viewbox Height="400" Width="400" BorderBrush="#FF000000" BorderThickness="1,1,1,1" Stretch="Uniform" StretchDirection="Both">
<SilverlightControlsNovember2008:PenguinControl/>
</controls:Viewbox>
</Border>
ViewBox with Button
ViewBox might not be appropriate for all controls, as it’s primary use is 2D vector graphics.
You would probably not have a lot of scenarios to use it in a LOB app.
Viewbox essentially does a lot of math for you and sets the appropriate ScaleTransform on the nested Child.
Scenario: we have a Button sized 50x50 pixels and we’d like to scale it to 250x250 pixels.
Here’s the original button and it’s XAML:
<Button Height="50" Width="50" Content="Button"/>
Option #1: Just change the Height & Width of the button!
<Button Height="250" Width="250" Content="Button"/>
Let’s also set an acceptable fontsize for this situation:
<Button Height="250" Width="250" Content="Button" FontSize="48" />
Option #2: Use a Viewbox
Hey, After all, we’ve just spent time learning about it.
<controls:Viewbox Height="250" Width="250">
<Button Height="50" Width="50" Content="Button"/>
</controls:Viewbox>
Looks kinda weird, doesn’t it?
All the lines are very thick and not really subtle, the spacing looks absurd, the color schema which used to look slick now looks overblown.
Option #3: set a ScaleTransform of times 5
<Button Height="50"Width="50" Content="Button" RenderTransformOrigin="0.5,0.5">
<Button.RenderTransform>
<TransformGroup>
<ScaleTransform ScaleX="5" ScaleY="5"/>
<SkewTransform/>
<RotateTransform/>
<TranslateTransform/>
</TransformGroup>
</Button.RenderTransform>
</Button>
We can see that the result of option #2 and option #3 are completely equivalent.
That’s because, as we said, ViewBox just does a lot of complex math on what ScaleTransform to use.
The question of using a ViewBox or not is better articulated by asking “Would I use a Scale trasnform here? or just set Layout properties?”.
-- Justin Angel
Microsoft Silverlight Program Manager