One of the most commonly requested features for Silverlight is some sort of 3d support. Within that, what many folks have expressed real interest in is the ability to do a perspective / non-affine transform of objects in space. Well, the Silverlight team delivered on that one for sure. Not only do we have the perspective transform (which could have been accomplished using shaders or the new bitmap API) but the controls in 3d space are still active and usable. Sweet!
Anyone who has messed with “real” 3d can tell you that just the setup required to get something usable can be a bit much. The perspective transform couldn’t be simpler, though. Here’s a simple form, with buttons, and some sliders for manipulating how the object floats in space. The three sliders control X, Y and Z rotation. You could use an image or any other element, but I wanted to show that controls are still live in 3d space.
What makes this possible is the new Projection attribute:
<Border.Projection>
<PlaneProjection x:Name="Projection"
CenterOfRotationX="0.5"
CenterOfRotationY="0.5"
CenterOfRotationZ="0.5" />
</Border.Projection>
Projection lets you define the center of rotation, global/local offsets and of course the rotation angles
No transform
Example X, Y, Z rotation
Reversed rotation
Look, I can still type in the field
Since I used the new Element to Element binding, there’s zero code in this demo; it’s all in the xaml. Here’s the xaml you can paste into your own control. Note I’m using three new features of Silverlight 3:
- Element to Element binding
- Based-On Styles
- Perspective Transform (Projection)
<Grid x:Name="LayoutRoot">
<Grid.Background>
<LinearGradientBrush StartPoint="0,0"
EndPoint="0,1">
<GradientStop Offset="0.0"
Color="Black" />
<GradientStop Offset="0.55"
Color="#FF001133" />
<GradientStop Offset="0.58"
Color="#FF000000" />
<GradientStop Offset="1.0"
Color="#FF050505" />
</LinearGradientBrush>
</Grid.Background>
<Border CornerRadius="5"
BorderBrush="DarkGray"
BorderThickness="2"
Background="White"
HorizontalAlignment="Center"
VerticalAlignment="Center">
<Grid Margin="10">
<Grid.Resources>
<Style x:Key="ElementInGrid"
TargetType="FrameworkElement">
<Setter Property="VerticalAlignment"
Value="Center" />
<Setter Property="HorizontalAlignment"
Value="Stretch" />
<Setter Property="Margin"
Value="2 2 2 2" />
</Style>
<Style x:Key="FieldLabelStyle"
TargetType="TextBlock"
BasedOn="{StaticResource ElementInGrid}">
<Setter Property="TextAlignment"
Value="Right" />
<Setter Property="Margin"
Value="2 2 8 2" />
</Style>
</Grid.Resources>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="80" />
<ColumnDefinition Width="200" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="28" />
<RowDefinition Height="28" />
<RowDefinition Height="28" />
<RowDefinition Height="38" />
</Grid.RowDefinitions>
<TextBlock Grid.Row="0"
Grid.Column="0"
Style="{StaticResource FieldLabelStyle}"
Text="First Name" />
<TextBox Grid.Row="0"
Grid.Column="1"
Style="{StaticResource ElementInGrid}"
Text="Pete" />
<TextBlock Grid.Row="1"
Grid.Column="0"
Style="{StaticResource FieldLabelStyle}"
Text="Last Name" />
<TextBox Grid.Row="1"
Grid.Column="1"
Style="{StaticResource ElementInGrid}"
Text="Brown" />
<TextBlock Grid.Row="2"
Grid.Column="0"
Style="{StaticResource FieldLabelStyle}"
Text="Url" />
<TextBox Grid.Row="2"
Grid.Column="1"
Style="{StaticResource ElementInGrid}"
Text="http://www.irritatedVowel.com/Blog" />
<Button Grid.Row="3"
Grid.Column="1"
Margin="5"
Style="{StaticResource ElementInGrid}"
Content="Save" />
</Grid>
<!-- this is the new projection attribute -->
<Border.Projection>
<PlaneProjection x:Name="Projection"
CenterOfRotationX="0.5"
CenterOfRotationY="0.5"
CenterOfRotationZ="0.5" />
</Border.Projection>
</Border>
<!-- Knobs for changing the X/Y/Z rotation. note the use
of element binding here to eliminate code from this sample -->
<StackPanel Width="150">
<Slider x:Name="XSlider"
Minimum="-360"
Maximum="360"
Margin="5"
Value="{Binding RotationX, Mode=TwoWay, ElementName=Projection}"
SmallChange="1" />
<Slider x:Name="YSlider"
Minimum="-360"
Maximum="360"
Margin="5"
Value="{Binding RotationY, Mode=TwoWay, ElementName=Projection}"
SmallChange="1" />
<Slider x:Name="ZSlider"
Minimum="-360"
Maximum="360"
Margin="5"
Value="{Binding RotationZ, Mode=TwoWay, ElementName=Projection}"
SmallChange="1" />
</StackPanel>
</Grid>
That’s all there is to it :)