Notes for building/running the sample project
- You need to have Visual Studio 2005 as well as the Orcas tools for VS 2005. Alternatively you can also use Expression to run the sample.
- You need the June CTP version of the .NET 3.0 Runtime as well the SDK.
- You need to change the path to the image in the XAML to point to a valid image file.
- If you have an earlier or later CTP, most of the code should still compile the same - though you may have to make some namespace changes.
|
Introduction
Welcome to this, our first joint article on WPF, which is part of WinFX ( now called .NET 3.0, for reasons that are totally unclear to anyone with half a brain ). In this article we will cover transformations, both in XAML and in code. While this is a logical 'starting' topic, even here WPF shows its power over traditional windows programming, as these transformations can be applied to any element in your UI, creating effects that would have been close to impossible in traditional forms programming. The first part of the article will run you thgrough some transforms in XAML, and the second part will demonstrate a sample app that shows an image and allows you to set transform values with sliders, with the idea that being able to interact in this way will give you a stronger idea of what the transforms can do, both together and individually.
Rotation Transforms
The RotateTransform
class is used to rotate a WPF object in an X-Y plane. It can be applied via XAML or directly via imperative code. For our examples, we'll use a WPF ListBox
control and we'll apply a Rotation Transform to it. Here's what the ListBox
would look like when a Rotation Transform of -20 degrees (20 degrees anti-clockwise) is applied to it.
Compare the ListBox
on the left which is rotated, with the one on the right (identical except that it's not rotated). And here's the XAML that achieves this.
<ListBox Name="listBox1" FontSize="15"
Canvas.Bottom="140" Canvas.Left="30"
Canvas.Right="150" Canvas.Top="40">
<ListBoxItem IsSelected="True">Canada</ListBoxItem>
. . .
<ListBoxItem>Spain</ListBoxItem>
<ListBox.RenderTransform> *(1)*
<RotateTransform Angle="-20"
CenterX="50" CenterY="50" /> *(2)*
</ListBox.RenderTransform>
</ListBox>
- (1) - We specify a
RenderTransform
on the object, which modifies how the object is rendered by the UI system. (This is in contrast to the LayoutTransform
which doesn't affect rendering, but affects how the object is processed by the layout system)
- (2) - We use a
RotateTransform
which lets us specify the angle of rotation in clock wise degrees. We can also optionally specify the X,Y point about which the object is rotated. Setting X and Y points equal to Width/2 and Height/2 will cause an object to 'spin' as opposed to rotate around an imaginary curve.
Later, we'll write a more flexible demo that demonstrates various transforms and how various properties can be applied to each of those transforms. Next let's look at a Scale Transform.
Scale Transforms
In the above screenshot, you can see a Scale Transform that has stretched the ListBox
on the left by 1.5 times in the X axis, and compressed it by 0.75 times in the Y axis. Here's the XAML that achieves this.
<ListBox.RenderTransform>
<ScaleTransform ScaleX="1.5" ScaleY="0.75"/>
</ListBox.RenderTransform>
We just replaced the Rotation Transform with a Scale Transform. Note that it's possible to apply multiple transforms on a single object, and we'll see that later. But before that, let's take a look at using the SkewTransform
class.
Skew Transforms
Skew Transforms skew an object by specified X and Y angles, and can be used to create some fancy transformation effects (and are also used for animations). The following screenshot shows a skew transformation of 20 degrees in the X axis and 30 degrees in the Y axis.
Here's the XAML code that achieves the above transformation.
<ListBox.RenderTransform>
<SkewTransform AngleX="20" AngleY="30"/>
</ListBox.RenderTransform>
Thus far we've only looked at single transformations on an object, next we'll look at how to achieve multiple transformations on an object.
Applying multiple transformations on an object
In the below example, we are going to apply two transforms on an object :
- A
ScaleTransform
to mirror-invert the ListBox
- A
TranslateTransform
to move the object back to the visible window
Here's what the final effect looks like.
And here's the code that achieves the above effect.
<ListBox.RenderTransform>
<TransformGroup>
<ScaleTransform ScaleX="-1" ScaleY="1" />
<TranslateTransform X="100" />
</TransformGroup>
</ListBox.RenderTransform>
Notice the use of the TransformGroup
object that allows us to group multiple transforms and then apply them on a single object. Here's another example of multiple transforms on the list box, which tries to achieve a 3d effect.
The XAML for the above effect is shown below.
<ListBox.RenderTransform>
<TransformGroup>
<RotateTransform Angle="15" CenterY="30" />
<SkewTransform AngleY="-45" />
</TransformGroup>
</ListBox.RenderTransform>
A sample app to play around with transformations
Ok, now that we know how to do transforms, let's write a little app that we can use to interact with them, and see how they work. The sample app has an image in it, and here's how it is defined using XAML. You will need to modify the ImageSource
property of the ImageBrush
element to point to an image on your local hard disk. In the zip download, we have included the JPG image that we used, so you could use that if you want to.
<Rectangle Stroke="#FF000000" HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Margin="198,80,260,230" Width="Auto" Height="Auto" x:Name="rect">
<Rectangle.Fill>
<ImageBrush ImageSource="d:\toofast.jpg" />
</Rectangle.Fill>
</Rectangle>
You probably realize that we can define our transforms inside the XAML, but we're trying to show some operations within the code. So, we define the transforms like this:
private TransformGroup trGrp;
private SkewTransform trSkw;
private RotateTransform trRot;
private TranslateTransform trTns;
private ScaleTransform trScl;
public Scene1()
{
this.InitializeComponent();
trSkw = new SkewTransform(0, 0);
trRot = new RotateTransform(0);
trTns = new TranslateTransform(0, 0);
trScl = new ScaleTransform(1, 1);
trGrp = new TransformGroup();
trGrp.Children.Add(trSkw);
trGrp.Children.Add(trRot);
trGrp.Children.Add(trTns);
trGrp.Children.Add(trScl);
}
This code creates the four possible transform objects and adds them to a TransformGroup
. This group is now a member variable, it's initialized, but it's not associated with our object. To do this, we need to add the following to the opening tag for our XAML document :
Loaded="OnSceneLoaded"
This has the effect of defining a method that will be called when the scene loads, and that method is defined as follows:
private void OnSceneLoaded(object sender, System.Windows.RoutedEventArgs e)
{
rect.RenderTransform = trGrp;
slSclX.Value = slSclY.Value = 1;
}
As you can see, this method assigns the transform group to the rectangle object, and sets initial slider values to the scaling sliders. The rest of the program logic is pretty simple : we just define a single method which sets all the transform values based on the various sliders, and hook all the value changed events on the sliders to the one method. An example XAML snippet goes like this:
<Slider LargeChange="0.1" Maximum="50" Minimum="-50" SmallChange="0.1"
Name="slSkwX" ValueChanged="OnValueChanged" />
Some attributes have been removed to make the example clearer. Now we just need that central method:
protected void OnValueChanged(object sender,
System.Windows.RoutedEventArgs e)
{
trRot.Angle = slRot.Value;
trRot.CenterX = slRotX.Value;
trRot.CenterY = slRotY.Value;
trScl.ScaleX = slSclX.Value;
trScl.ScaleY = slSclY.Value;
trScl.CenterX = slSclOX.Value;
trScl.CenterY = slSclOY.Value;
trSkw.AngleX = slSkwX.Value;
trSkw.AngleY = slSkwY.Value;
trSkw.CenterX = slSkwOX.Value;
trSkw.CenterY = slSkwOY.Value;
trTns.X = slTrnX.Value;
trTns.Y = slTrnY.Value;
}
Now all our transform values will be set whenever you move any slider. This is arguably inefficient, but it does simplify the code for this example.
Conclusion
As was stated in the introduction, the real power of WPF becomes apparent pretty quickly, even this simple example shows off things that under 'traditional' development would be a lot of work. However, we've really only just begun, our next article on animation will really start to show what's possible with this new framework.
History
- Jul 21, 2006 - Article first published