Monday, September 25, 2006

WPF for beginners: Hello world

I've decided to start a WPF blog by diving into a working app, that shows off a few features. It couldn't be a WPF Hello World without a few fancy features, so the app shows off a couple of the new styles and animations. In itself it's amazing functionality without 3rd party products, but is all the more impressive when you learn there is no code behind at all. And this is the tip of the iceberg. WPF XAML has a lot of depth, and prove once again that Microsoft is giving programmers the tools they need, and it's up to us to make the most of those tools now. In a way, it would have been ideal if XAML was the first windows forms platform for .net, as there will be few doubters once you give the XAML a go.

Getting started:
  1. In Visual Studio 2005 goto File -> New -> Project from the main menu
  2. Select Project Type "NET Framework 3.0"
  3. Choose "Windows Application (WPF)" (note that XAML Browser Application would be near identical code, except will run in a browser)
  4. Change the name and location if needed and press OK.

XAML

XAML is the new forms designer technology for WPF. I'm not going to go into detail about XAML, but there are some cool features you'll see in these examples. Firstly, controls have become infinitely more flexible than Winforms. Take for example a Button. Like Winforms you have basic properties like Background and Foreground colors, and can simply put some text in between the tags and have a button. Add a Click event and off you go ... BUT XAML allows you to take that text away and put a Panel in the Button tags. And in that panel, you can put other controls. If you want your button to have 3 images side by side, with text going diagonally across the images, you can. Not only for buttons, most controls allow this feature. The "content" area of a button or other controls only allow one child, but once that child is a panel, you suddenly open up a whole new world.

Another cool feature is the Properties design. Within your Button tag, you can simply put a Background="White" to set the Background property, much like Winforms. But again, you can open this up, but adding a tag after the Button tag, like <Button.Background>. Inside those tags, you suddenly have a lot of freedom again, and can add images, gradients, etc. Other Properties can do other sorts of things too, and in many cases I've found it beneficial adding these tags outside the main tag, as flexibility follows.

Also remember that XAML has inheritence built in nicely. Controls can inherit properties from the parent controls, which is handy for databinding etc. Styles also can inherit from other styles, which makes building a consistant but complicated GUI relatively easy. And again with animations, like scaling a control to be bigger, child object simply scale with it, and even other animations will apply over that new size.

Resources

Very widely used, we can store styles in a Resource section, in this case <Window.Resources>, which would most likely come straight after the root Window tag. In XAML, these resources can be referenced by calling StaticResource and using the x:Key name. So if we have a Style in the Windows.Resources section, we need to give it a key like <Style Key="StandardButtonStyle">. To use this style <Button Style="{StaticResource StandardButtonStyle}">.

Style

A lot of power comes from our style objects. You can take these as far as you want to really. Like CSS stylesheets for HTML we can setup standard looks for our controls, setup styles to target specific control types (ie. all buttons on a form), inherit from other styles and even triggers styles and animations from events and properties of the control. What was formerly not possible, or at least very tricky with lots of code, can now take only a few lines of XAML and effect many controls.

As mentioned earlier a Style must have an x:Key so that other controls can find it using StaticResource. Also mentioned we can inherit from another style using BasedOn="{StaticResource EarlierStyle}". This allows you to build styles from a basic look adding extra functionality as you go up. A style can also target a type of items like this TargetType="x:Type Button". Note that if you target a type, you can't inherit from a style that targeted another type, so take care in building your styles up properly.

Setters give you the most basic functionality in a style. <Setter Property="Background" value="Red"> will cause any control that uses that style and doesn't override Background to have a Red background. By splitting Value out into another tag you can also get more advanced behaviour <Setter Property="Background"> <Setter.Value> <LinearGradientBrush>

Triggers allow you to change the look, or add animations when properties are certain values or events are triggered. By allowing us to do a lot more visual work in the XAML, we are getting closer to the true separation of GUI and code behind. In Winforms we have a lot of code behind to keep the application looking right, but with XAML it's more than likely possible to keep most of that code in nice clean XAML.

To perform an animation based on an event like Loaded or Click <Style.Triggers> <EventTrigger RoutedEvent="Click"> <EventTrigger.Actions> <BeginStoryboard> <Storyboard> <ColorAnimation>

To have a Trigger based on a property choose <Trigger Property="Control.IsMouseOver" Value="true"> <Setter Property="Background" Value="Red"> <Trigger.EnterActions>... <Trigger.ExitActions>. In this case when the property IsMouseOver is true, we can change the background to Red, and when it's false, it will use the default style. Also by adding tags EnterActions and ExitActions we can start animations when the property becomes true and when it becomes false.

Very cool.

Effects

I have only touched upon effects in this example, but like styles, effects are available and work the same for most controls. The beauty of WPF is you learn how to use an effect, then you can use that effect on buttons, panels, textbox's etc, as effects are applicable to the UIElement on which all those controls are based upon. Many effects add to the GUI feature set, such as a blur effect, glow, drop shadow, bevel, emboss. These can also be animated, as shown in this example.

Animation

In XAML, we again have struck gold with animations that can be done without writing a line of code behind or using timers. WPF uses it's internal timeline to get property values from one value to another. And once again, proeperty is fairly vague allowing us to simply make a control disappear by setting it's Opacity from 1 to 0, or it can be setting complex transformations of the control, parallel to other property changes.

To setup an animation we use the Storyboard seen earlier <BeginStoryboard> <Storyboard> <DoubleAnimation TargetName="CloseButton" TargetProperty="Opacity" From="1" to="0" duration="0:0:0.5" autoreverse="false"> ... In this case DoubleAnimation indicates From and To must be a Double, which could be basically <Type>Animation where Type could be Int32, Byte, Color etc. From and To are start and end values, Duration is a time (0.5 seconds here) and AutoReverse indicates once it's finished the animation will happen in the other direction. Also set RepeatBehavior="Forever" if you want this animation to continue. By adding a tag under <Storyboard> for <ParellelTimeline>, we can run several animations at once, so we can make the Border and Foreground change color at the same time.

Transform

The last feature I've added to this sample shows a simple ScaleTransform when a button is clicked. We can rotate, skew or scale using <Button.RenderTransform> after the Button tag, and make it dead easy to give our app a 3D feel.

If you want more than one Transform, your next tag should be <TransformGroup> otherwise add the Transformation tag <ScaleTransform ScaleX="1" ScaleY="1.5">, which in this case stretches the height of the control and all children. You can again animate this by using <DoubleAnimation TargetProperty="RenderTransform.ScaleX" From="1" To="-1">, which will take the width from normal to nothing, to normal but with the control backwards.

Note that the control you are animating must have a ScaleTransform tag for this to work.

Example

There is more detail within this example, but I'm hoping this is easily enough to get your feet wet and start thinking about what is possible with XAML. Enjoy:





<Window x:Class="HelloWorld.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Hello World" Height="500" Width="500"
>
<Window.Resources>
<!-- General style -->
<Style x:Key="GrayGradientStyle" >
<Setter Property="Control.Background">
<!-- Set a standard gray vertical gradient style, which will be inherited -->
<Setter.Value>
<LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
<GradientStop Color="LightGray" Offset="0" />
<GradientStop Color="DarkGray" Offset="0.7" />
<GradientStop Color="Silver" Offset="1" />
</LinearGradientBrush>
</Setter.Value>
</Setter>
</Style>

<!-- Glow effect used by next style -->
<OuterGlowBitmapEffect x:Key="OuterGlow" x:Name="OuterGlow" GlowColor="Yellow" GlowSize="0" />

<!-- Style to apply for all buttons, inherits GrayGradientStyle -->
<Style x:Key="{x:Type Button}" BasedOn="{StaticResource GrayGradientStyle}" TargetType="{x:Type Button}" >
<Setter Property="Opacity" Value="0.5" />
<Setter Property="Margin" Value="20" />
<Setter Property="Width" Value="100" />
<Setter Property="BitmapEffect" Value="{StaticResource OuterGlow}" />
<Style.Triggers>
<Trigger Property="Button.IsMouseOver" Value="true">
<Setter Property="Foreground" Value="Red" />
<!-- When IsMouseOver is set to true, set the button to full Opacity and show a Yellow glow around the button -->
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<ParallelTimeline>
<DoubleAnimation From="0.5" To="1" Duration="0:0:00.5" Storyboard.TargetProperty="Opacity"></DoubleAnimation>
<DoubleAnimation Storyboard.TargetProperty="BitmapEffect.GlowSize" AutoReverse="True" From="0" To="15" Duration="0:0:0.5" />
</ParallelTimeline>
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
<!-- When IsMouseOver is set back to false, set the button Opacity back to half -->
<Trigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation From="1" To="0.5" Duration="0:0:01" Storyboard.TargetProperty="Opacity"></DoubleAnimation>
</Storyboard>
</BeginStoryboard>
</Trigger.ExitActions>
</Trigger>
</Style.Triggers>
</Style>

<!-- Style to apply for the window -->
<Style x:Key="WindowBackground" TargetType="{x:Type Window}">
<Style.Triggers>
<!-- After the form is loaded continually cycle the background colors back and forth between these colors -->
<EventTrigger RoutedEvent="Window.Loaded">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard RepeatBehavior="Forever">
<ColorAnimation From="LightBlue" To="LightCoral" Storyboard.TargetProperty="Background.Color" Duration="0:0:10" Timeline.DesiredFrameRate="10" AutoReverse="True" />
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
</Style.Triggers>
</Style>

</Window.Resources>

<!-- Set the style to the above WindowBackground -->
<Window.Style>
<StaticResource ResourceKey="WindowBackground"></StaticResource>
</Window.Style>

<!-- Main controls -->
<StackPanel>
<StackPanel HorizontalAlignment="Center" >
<!-- Hello World Click Me button (automatically picks up Button style) -->
<Button>Click Me
<Button.Triggers>
<!-- When the button is clicked transform the scale of the Hello World border and turn it's opacity to full and back -->
<EventTrigger RoutedEvent="Button.Click">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<ParallelTimeline>
<DoubleAnimation Storyboard.TargetName="HelloBorder" Storyboard.TargetProperty="RenderTransform.ScaleX" From="0" To="1" Duration="0:0:5" AutoReverse="True" />
<DoubleAnimation Storyboard.TargetName="HelloBorder" Storyboard.TargetProperty="Opacity" From="0" To="1" Duration="0:0:5" AutoReverse="True" />
</ParallelTimeline>
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
</Button.Triggers>
</Button>
<!-- Hello World box -->
<Border Width="400" Height="300" Name="HelloBorder" BorderBrush="Black" BorderThickness="2" Opacity="0.3" Style="{StaticResource GrayGradientStyle}">
<Border.RenderTransform>
<!-- Will mean Border is invisible due to ScaleX being 0 -->
<ScaleTransform ScaleX="0" ScaleY="1" CenterX="200" />
</Border.RenderTransform>
<TextBlock Width="400" Height="300" Name="HelloText" TextWrapping="Wrap" FontSize="100" TextAlignment="Center">
<TextBlock.Foreground>
<!-- Set a nice radial gradient for the Hello World text -->
<RadialGradientBrush GradientOrigin="0.5,0.5" Center="0.5,0.5" RadiusX="1" RadiusY="1">
<GradientStop Color="Yellow" Offset="0.2" />
<GradientStop Color="Orange" Offset="0.5" />
<GradientStop Color="Red" Offset="1" />
</RadialGradientBrush>
</TextBlock.Foreground>
Hello World
</TextBlock>
</Border>
</StackPanel>
</StackPanel>
</Window>

kick it on DotNetKicks.com

2 comments:

Anonymous said...

What is the difference between Key="{x:Type Button}" and TargetType="{x:Type Button}"?

Anonymous said...

top [url=http://www.c-online-casino.co.uk/]uk online casino[/url] coincide the latest [url=http://www.casinolasvegass.com/]las vegas casino[/url] unshackled no deposit perk at the leading [url=http://www.baywatchcasino.com/]www.baywatchcasino.com
[/url].