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

Monday, September 18, 2006

Getting started on WPF and .net 3.0 RC1

I've downloaded Vista RC1 over the weekend and decided that Vista is getting very close, so it's definitely time to get cranking with .net 3.0. At this point in time information is scattered, confusing and light on, so I'll be hoping to help share my investigations with others out there. It seems like it's going to be a learning curve, but hopefully at the end it's worthwhile.

But for the moment, at RC1, it's probably still a little too risky to fully convert an important machine to Vista, so I'd suggest you get hold of a test machine. Below is some of the software you can use to get you up and running on a test PC, before the .net 3.0 and Vista become official versions.

Vista RC1
I'm very impressed with Vista RC1. It's clearly a "pretty" OS, up there with the latest Mac OS (which I've reluctantly used a bit) and any screenshots I've seen of the fancier Linux OS's. It's features are growing on me, but as a sceptic I'd still say for the vast majority of users there are mild improvements apart from visual ones. With tools like Media Center, DVD authoring, automatic mobile device recognition (eg my PDA) and calender built into the OS, it does avoid a bit of the countless software installs we are used to with a fresh OS, so there is something to be gained from getting Vista.

Go get it now here: http://www.microsoft.com/windowsvista/getready/preview.mspx

Windows Server 2003
Of course if you aren't willing to be a guinea pig for Microsoft, but still want a fresh, legal version of XP for a test machine to try out .net 3.0, Micrsoft is giving out a 6 month trial of Windows server 2003. The beauty of this is you get 6 months uninterupted development on a machine, without Vista complaining that the shipped version is out. You also get a stable fast OS that will run all the .net 3.0 goodness. Not a bad option for a test PC.

Windows Server 2003 180 day trial: http://www.microsoft.com/windowsserver2003/evaluation/trial/default.mspx

Visual Studio trial
Microsoft have of course outdone themselves with the Express editions of Visual Studio. And I'm sure these will include Visual WPF tools later on, but for now WPF programming is best done in Visual Studio from what I've been told.

Again if you don't have this already, Microsoft has 180 day trial for you:
http://msdn.microsoft.com/vstudio/products/trial/

Windows .net 3.0 SDK:
A must for developing applications in .net 3.0 is the SDK itself:
http://www.microsoft.com/downloads/details.aspx?FamilyId=117ECFD3-98AD-4D67-87D2-E95A8407FA86&displaylang=en

Visual Studio Extensions
The extensions for Visual Studio setup all the test projects, so rather than starting with empty projects you can start a new WPF project, which makes life that little bit easier.

The .net 3.0 Visual Studio extensions (RC1):
http://www.microsoft.com/downloads/details.aspx?FamilyId=935AABF9-D1D0-4FC9-B443-877D8EA6EAB8&displaylang=en

WPF
I'm more than interested in WPF than the rest of .net 3.0 at the moment, so here are a couple of tasters that I will be reading over the next few days/weeks also. I'm sure there is way more out there, but for now there is more than enough to get you started. I will be putting my own articles on this blog also, to hopefully go through some of the features in a simplified way.

Here is a nice article with an overview to what WPF is all about. There is no real detail, but it does help to get your head around what XAML will look like, and what WPF can do for you:
http://msdn.microsoft.com/windowsvista/reference/default.aspx?pull=/library/en-us/dnlong/html/introducingwpf.asp

This site gives a good all round look at WPF, with plenty of code samples and tutorials to get you started (click on Download Code... for the files and tutorials):
http://wpf.netfx3.com/

Of course all the other .net 3.0 technologies are here too:
http://www.netfx3.com/default.aspx

And one more link to the mother of information about WPF:
http://windowssdk.msdn.microsoft.com/en-us/library/ms754130.aspx
which can be found in your Windows SDK (hopefully this works on other machines):
ms-help://MS.MSSDK.1033/MS.NETFX30SDK.1033/wpf_conceptual/html/f667bd15-2134-41e9-b4af-5ced6fafab5d.htm

I am due to be looking at a lot of this over the next few weeks and will share my findings. Enjoy!

kick it on DotNetKicks.com

Tuesday, September 12, 2006

Microsoft AJAX is the new Atlas

It's worth a mention that Atlas is changing it's name and is release date is set to late 2006.

In short the new naming is like this:
"Microsoft AJAX Library" for all Atlas client controls
"ASP.NET 2.0 AJAX Extensions" for the asp.net server side Atlas controls
"ASP.NET AJAX Control Toolkit" for the current Atlas control toolkit


Of course I'm getting all this from the wonderful ScottGu blog article:
http://weblogs.asp.net/scottgu/archive/2006/09/11/_2200_Atlas_2200_-1.0-Naming-and-Roadmap.aspx

Why is it that this naming bugs me? Beta technologies always have the cool and unusual names, whereas the final product is usually so plain and boring.

I can only imagine that searches will be slower and more painful with this new naming. Put "Atlas C#" and other terms into a search engine you will get your results pretty easily. Put "Microsoft AJAX C#" or "ASP.NET AJAX C#" and you may be confused with other AJAX libraries or articles about AJAX as a technology etc. So with this naming we'll need rock solid intellisense and documentation otherwise our lives just got harder.

And to those that haven't followed the Atlas project, it would simply sound like Microsoft found that AJAX was taking off and quickly tacked a library together and and said "Hey, now we have AJAX... see Microsoft AJAX." The truth is the project has been very well thought out. A free client side library, a free asp.net library and a free community built toolkit. It sounds far too open source and cool to be Microsoft. At least too cool to be "Microsoft AJAX"! I see it as one of those projects, along with the wonderful express products, that really reflects Microsofts future potential as a helping hand to programmers, not an evil dictatorship set to make everyone learn the hard way to code with the highest money outlay.

Oh well, it's only a name in the end, the technology is still cool and I'd still tip it to be the number 1 AJAX library for .net shortly after it's release. Lets hope Scott and the others keep pushing the technology to great heights.


kick it on DotNetKicks.com

Tuesday, September 05, 2006

C# object arrays BinarySearch, Sort and IComparer

This isn't so much a new or very difficult thing, but it's something I've happened to come across twice recently, and I've needed to dig up the example code both times, so thought I'd add it to my blog for future reference.

Basically, I'm getting much more interested in dealing with object arrays rather than DataSets even in smaller projects. I feel like I have more control over certain things with object arrays, like business logic and customising properties. Now one of my projects was .net 2.0 so I used the generic List<> which was quite nicely put together, but my other project is a 1.1 project, so needed a slightly more outdated method.

I'll explain that one here, and probably add a note about the List<> way to do the same (plus more) next time.

Setting up the object array
For a very basic example I've got a class with a couple of fields in it for an Mp3 list:
public class Mp3Class {
private string _Artist;
private string _Song;
private int _Ranking;
public string Artist { get { return _Artist;} set {_Artist = value;}}
public string Song { get { return _Song;} set {_Song = value;}}
public int Ranking { get { return _Ranking;} set {_Ranking = value;}}
public Mp3Class (string Artist, string Song, int Ranking)
{
_Artist = Artist; _Song = Song; _Ranking = Ranking;
}
}

A basic example to get some data into this would be:
Mp3Class[] mp3s = new Mp3Class[5];
mp3s[0] =
new Mp3Class("Cave", "Ship Song", 9);
mp3s[1] =
new Mp3Class("Nirvana", "Smells like teen spirit", 3);
mp3s[2] =
new Mp3Class("Sonic Youth", "100%", 1);
mp3s[3] =
new Mp3Class("Cave", "Red right hand", 4);
mp3s[4] =
new Mp3Class("Nirvana", "Heart shaped box", 7);

The Array class
So the thing you really need to get your head around working with arrays of objects is the Array class. It's very similar to working with List<>, but missing a few features. For simple arrays, things like IndexOf, Sort and even BinarySearch can be straight forward, but with object arrays a little more work is needed, but fortunately not too much.

Array.Sort
For sorting our class, there is one big thing that makes it more difficult. Which property do you sort on? In this case all are valid, as you may be ordering by artist, by ranking or even by song, but that means we need to implement another class to help us out. The IComparer interface gives us the ability to sort things how we wish using our objects. Basically, if we want to sort this list by Artist, we can implement an IComparer class which compares the Artist string, and Sort will do the rest, or if we want to sort by Ranking, we can compare the ranking numbers and Sort will do the rest. Here are those two examples:

public class ArtistCompareClass : IComparer
{
int IComparer.Compare(object x, object y)
{
int RetVal = String.Compare((x as Mp3Class).Artist, (y as Mp3Class).Artist, true);
return RetVal;
}
}

public class RankingCompareClass : IComparer
{
int IComparer.Compare(object x, object y)
{
int RetVal = (x as Mp3Class).Ranking - (x as Mp3Class).Ranking;
return RetVal;
}
}

The ArtistCompareClass cheats by using String.Compare (with case insensitivity). IComparer.Compare is simply looking for a negative if the first object should be ordered earlier, 0 if they are the same and a positive number if the 2nd object should be ordered earlier. Passing this object to Array.Sort will sort the array alphabetically by Artist.

The RankingCompareClass implements the same IComparer.Compare, so again, if the first Ranking is smaller it returns a negative, if they are the same a 0, if the 2nd Ranking is smaller a positive. This will order the object array by Ranking.

The call to sort the array will look like this:
Array.Sort(mp3s, new RankingCompareClass());

You now have the mp3s ordered by Ranking.

Array.BinarySearch
The BinarySearch as the name suggests searches the array using the same IComparer (or at least can do it this way). So again IComparer helps us search through an array of objects, when the language would otherwise have no idea of what it's searching for.

Note: Remember these key things when using a BinarySearch:

  • You must sort by the IComparer before using BinarySearch
  • It won't work (100%) if your "key" is not unique
  • If the result is negative, you can do a bitwise operation to see the closest match
  • If you are only searching a subset of the array, it will still return the item in the entire array that's a match.

Here are a couple of examples. I'll give you another IComparer which will help do "real" searches easier. In this case we'll send through the ranking as the 2nd parameter rather than a full object. As you can imagine if someone wanted to search for the song with ranking number 5, it's easier to pass through 5 than new Mp3Class("", "", 5):

public class RankingCompareValueClass : IComparer
{
int IComparer.Compare(object x, object y)
{
int RetVal = (x as Mp3Class).Ranking - (int)y;
return RetVal;

}

}

To find the object which matches a search with a ranking of 4 (MatchingObject is 2, which is the 3rd object in the array):
Array.Sort(mp3s, new RankingCompareClass());
int MatchingObject = Array.BinarySearch(mp3s, 5, new RankingCompareValueClass());
MessageBox.Show(mp3s[MatchingObject].Artist + " - " + mp3s[MatchingObject].Song);

If the user tried to search for a ranking of 5, we could take them to the next closest match (MatchingObject is 3, which is the 4th object in the array):
Array.Sort(mp3s, new RankingCompareClass());
int MatchingObject = Array.BinarySearch(mp3s, 5, new RankingCompareValueClass());
if (MatchingObject < matchingobject =" ~MatchingObject;">" - " + mp3s[MatchingObject].Song);

That's about it. It's worth pointing out that List<> does most of this stuff plus more, and is slightly easier to use, so I'd tend to use that for any 2.0+ projects.


kick it on DotNetKicks.com