Friday, November 11, 2011

WPF Tools and Examples


Download Code

Introduction

eXtensible Application Markup Language (XAML, pronounced ZAMMEL) is coming, you better believe it. For those that haven't heard of XAML, it is the way to code for Microsoft's new presentation layer, Windows PresentationFoundation (WPF, codenamed Avalon).
XAML is an XML style markup language; the XAML markup is responsible for the presentation of the graphical elements, much the same as HTML markup.
XAML code can be developed using a multitude of tools, such as Zam3D, XAMLPad which is provided with the .NET Framework 3.0 SDK, Visual Studio 2005, and the new Expression Blend (previously Expression Interactive Designer).
One of the main ideas behind XAML (so I think anyway) is that the graphical front end can be developed by someone with an arty nature, and then handed back to a developer who can import the arty code (the XAML) and then code the backend .NET code to drive the interface. Both the graphical designer and the software developer should be able to freely transfer work using the same language, XAML.
That's the basic idea anyway, but this is not what I am trying to put across in this article. In this article, I want to showcase some of the concepts of what can be done in XAML, and what tools one can use for creating XAML, and also how these tools may be used. I will also be explaining how I achieved all the different elements of the demo application.
I will not be talking about how to hand-code XAML, I will be purely focusing on some of the most popular (I think anyway) XAML authoring tools.
If you really don't know your XAML from a camel, I suggest you check out Marc Clifton's article at XAML Resourceswhich should bring you up to speed.
OK, so let's break down what this article will cover:
  1. Cider
  2. Brief discussion on the CIDER Visual Studio add-in, which enables VS users to create XAML designs simply by using drag and drop from the toolboxes, as we would for a .NET 2.0 project.
  3. Expression Blend (previously Expression Interactive Designer, and codenamed Sparkle)
  4. Brief introduction into what Expression Blend looks like.
  5. Using Expression Blend to create the demo application (attached)
  6. Dissection of the demo app, and how it fits with Blend.
  7. Visual Studio 2005 integration
  8. Taking a project from Blend (EID, Sparkle) into Visual Studio 2005. This should be enough to get us going, so let's march on.
  9. The code
  10. Yeah, the techie bit, the code.

Article Code Description

I only seriously started looking into XAML about two days ago (before that, I had had a cursory glance and a wink at it, but nothing serious).
For this article, I decided to try and create an application using the existing tools out there; after all, XAML is being generated by tools right now, so why not use them? I am not saying that we all won't need to know XAML; we will, that's a given, but before I sat myself down to read my copy of Charles Petzold's book Applications = Code + Markup(all 1002 pages of it), I just wanted to see what I could achieve with the tools, as this is probably what most people will be using in their day to day activities.
So this article can be seen as one man's journey into the unknown. I am pretty happy with the end product. In fact, I will briefly describe what I managed to achieve. So what does the attached app do? Well, it showcases the following XAML concepts:
Attached application functionality:
  • Custom templates for button controls
  • Custom template for slider control
  • Databinding between slider and a textbox
  • Alpha shading on textboxes
  • Animations of button on mouse overs
  • Animations of text on mouse overs
  • Playing of multimedia of the user's choice
I will give you a sneaky peak at what the application looks like when it's running, though it's not that obvious what is animated and what is not from a static screenshot. Later on, I will describe each of the application functionality points in more detail, with screenshots where needed.
This may not look too impressive to some of you, but I assure you there is lots of neat stuff here; you will need to play with the real application to see what is animated and how it all fits together. You can download the app later; for now, let's read on and have a look at some XAML fun.

Prerequisites

Before you read on, you should ensure as a minimum you have the following items if you want to run the attached application:
  • Microsoft .NET Framework 3.0
  • Visual Studio 2005
  • A good fat machine with plenty fo RAM (yes, lots, 1 GB plus; I have 1GB and it's only just OK)
And optionally, Cider, if you want to have a look at that, which is available at "Cider" - The Visual Studio Designer for WPF ("Avalon").

1. Cider

I've decided to start this article discussion with a brief intro into WPF via the use of Cider, which is a Visual Studio add-in for creating WPF applications. By looking at Cider first, I am hoping that you will be broken into the world of WPF slowly, and will be eager enough to read on to some of the other more interesting parts of this article.
OK, so once you have all the prerequisites installed, and have installed Cider, you will have the option to create a new WPF application within Visual Studio. Let's have a look at that, shall we?
We can see that one of the project options when starting Visual Studio is now a WPF project, as shown in the figure below:
So if I choose to accept this, we'll continue to create a WPF application.
We can see that once Visual Studio loads, we have some new tabs within the normal interface; we now have a Design/XAML tab, as shown here.
What this tab allows us to do is view the XAML code or the GUI design (probably stating the obvious here, but there you go).
Let's just have a look at the boiler plate code that creating a new WPF application gives us.
Visual Studio / Cider gives us the following XAML for free, which is called Window1.XAML:
So this is the XAML markup to describe the user interface, but shouldn't there be some code somewhere, written in something we have a hope of understanding? Well yes, there is, it's a .NET code file (C# in my case).
And it looks like the following code snippet (Window1.XAML.cs):
This is not too scary now, is it? In fact, any of you that has done ASP.NET should be fairly happy with the idea of a code-behind file. This is fairly similar, isn't it? Basically, all the presentation components and their markup are within one file (.XAML), and the code to do the logic is in another code file (.XAML.CS, or .XAML.VB if you are using VB.NET).
So the next step would be to actually add some components to the page. How do we go about doing that?
Providing we have the Design tab selected, the toolbox will contain numerous controls that may all be dragged to the form's workspace area (I have noticed that some people also call this the artboard area).
This example shows that I have placed a single WrapPanel, which when placed would have been created like
With the start and closing of the element all on the same line. This is pretty useless, as the WrapPanel is a container; so how do we make it contain other controls? Well, I selected the XAML tab (which will clear the toolbox of controls; don't worry, they come back in the Design tab view) and split the WrapPanel element out so the start and close parts of it are on different lines. Then I moved the mouse into the area between the start and close of theWrapPanel element, switched to Design tab view, and dragged some more controls (buttons) from the toolbox into the WrapPanel. And hey presto, the WrapPanel then contained the buttons, and correctly wrapped them as it should.
It should be noted that using Cider also allows us to click on a control and change its properties, as we would a .NET 2.0 application, by the use of the Properties window. But be warned, the properties are not the same, and some classes are even different; the MessageBox, for example, expects new arguments, and returns a newMessageBoxResult object. So be wary of that.
This is obviously a bit of a contrived example; the idea is that it's a brief intro into Cider. It's up to you to experiment with this stuff; this is simply showing you that you can do this stuff within a nice, safe, familiar environment, namely Visual Studio (ah, our friend).
Are you still feeling brave, and want more XAML concepts, and want to know how to use some of the other XAML resources available? Then, read on.

2. Expression Blend

Some time ago, I downloaded the July CTP of Expression Interactive Designer (code name Sparkle, A.K.A. Flash Killer). This product is now available as a BETA release, under the new name Expression Blend.
In my opinion, Expression Blend will be the core tool used to construct creative XAML projects. Blend empowers (and no, I'm not being paid to write this for Microsoft) graphical designers and coders to share the same language, any takers as to what these may be. Camle you say, no, not quite. It's XAML.
Using Blend, the graphic designers can do their magic, and then hand the entire project to the developer who can then add the .NET code to the application, for example, to query a database, access a Web Service etc.
Of course, if you fancy yourself as a bit of an arty dev, you could also dive into Blend; it's quite fun, I think. I've only been using it two days, and think it's fairly easy to use. With the exception of, binding to an external XML data source, which worked 100% in static view, but would not render in run time. Grrrr. Never mind, so apart from that one gripe, it has all worked out, I think.
What this section is going to do is discuss the basic layout of Blend (I originally published this article using screenshots done using the July CTP of Expression Interactive Designer, which some folks didn't like, as Blend is available for free download for a 30 day period, so I've updated this section of the article for those folks and new folks).
So let's start, shall we?
What does Blend look like? Well, when you start it, it will look something like the following diagram. Microsoft seems to be following the new Office 2007 idea, that the palettes are organized based on categories; for example, to change how something looks, we would use the Appearance panel. Where as in normal Visual Studio 2005 (.NET 2.0 project), we would simply have a list of all the properties, and find the one we want and change it.
As can be seen, there are several work areas and numerous palettes that make up the environment; let's briefly discuss these:

Artspace

This is the main area, where the design will be shown; you may drag and drop .NET controls onto this surface from the Library Palette, or you may add the controls manually using the XAML tab.

Objects and Timeline Palette

Remembering that XAML is an XML type language, it can be thought of as a tree. This palette shows the document hierarchy, and allows the quick navigation to a document element by double clicking on the element name in the document tree. From this palette, we can also create animations by using timelines. More on this later.

Tools Palette

This palette simply allow the user to grab, zoom, draw, fill, etc.

Projects Palette

This palette simply shows all the project files and references etc.

Library Palette

This palette shows all the .NET Framework 3.0 controls, which may be dragged onto the workspace. This is available by clicking on the down arrow at the bottom of the tools palette.

Brushes Palette

This palette allows the changing of colors and brush type / style etc.

Appearance Palette

This palette allows the appearance of a selected item to be changed.

Data Palette

This palette allows external data sources to be added to the project, such as XML data sources, or standard CLR data sources, such as DataSet etc. Once these DataSources have been added to the project, they may be used to bind data against.

Layout Palette

This palette allows layout changes to be applied to the selected item, such as size margin, as shown above.

Common Properties Palette

This palette allows the modification of items such as the tooltip, enabled state, cursor etc.

Text Palette

This palette allows the text properties of the selected text to be changed.

Transform Palette

This palette allows the current object to be manipulated. It provides scaling, rotating, skew, flip, etc.

Timeline Triggers Palette

This palette allows us to control a timeline based on a trigger. This is probably my favourite of the palettes, as it allows us to trigger animations. More on this in a minute.
So that is the basic palettes covered. I hope that is a gentle introduction into Blend.
Yeah great man, but how do we use it, to do something useful, I hear you say.
In the next subsection, I am going to include screenshots of the attached demo application, and talk about how each part was achieved, and finally, I will include both the XAML (as was generated by Expression Blend) and the Visual Studio 2005 modified .XAML.cs C# code-behind file.

3. Using Expression Blend to Create the Demo Application (Attached)

Recall from the introduction that I stated the attached demo application functionality would be as follows:
  • Custom templates for button controls
  • Custom template for slider control
  • Databinding between slider and a textbox
  • Alpha shading on textboxes
  • Animations of button on mouse overs
  • Animations of text on mouse overs
  • Playing of multimedia of the user's choice
So how did I do that? Let's look at them one by one.

Custom Templates for Button Controls

One of the nice features of XAML is the ability to effectively re-skin any of the standard .NET 3.0 Framework controls. If you don't like what it looks like out of the library, simply change its template. That is exactly what the demo application does. Let's look at that.
In this example, I have simply placed a .NET 3.0 Framework Button control onto the workspace. I then choose to "Edit a Copy of the Template".
This results in a new hierarchy tree being shown for the button template we are editing.
What this hierarchy tree is actually showing is the objects that actually make up the default button rendering (as it comes out of the box (Library palette)). To change the way we want the buttons to look, we simply change the objects contained in the button's template tree. This is easily achieved; we can simply clear the current template objects, or simply re-style the ones that are there.
I opted for completely new objects. Let's have a look at that then, shall we?
It can be seen that I actually introduced a Grid object, which has two children elements:
  • ContentPresenter object which is used to show the buttons content (this could be text, images, video, etc.)
  • Rectangle object which has smooth corners applied to create the rounded button
It can also be seen that there are several button states, each of these can have a different effect on the new button's template objects. For example, on the IsMouseOver state, I could have a blue background, and on theIsPressed state, I could have a yellow background. New states may be added using the "Add Property Trigger" button within the "Objects and Timeline" palette. From here, you will need to pick which property should fire the trigger. A list is available, as shown here.
Let's have a look at one of these. Let's say, we want to create a trigger on the width being equal to 15. Well, we use the "Add Property Trigger" button, find the width property, and type 15 into the box that we have available to us.
From there, you may then transform the selected object in any manner you see fit.
The result of all this messing about is that we now have a button which looks like the following figure. It is really a standard .NET Framework button, but it just looks like we want it to look. This is what Templates are all about.
The image in this case maps to the ContentPresenter object within the button's template, and the curved box is the smoothed Rectangle within the button's template.

Custom Template for Slider Control

This was done exactly as the button templating; the new slider rendering is as shown below:

Databinding Between Slider and a Textbox

One of the nice features of .NET 3.0 / XAML is that you can bind the value of one control to the property to another. In the attached demo application, I have attached the value of the slider to a textbox's font size. Let's have a look at how to do this.
In the attached project, I have bound a textbox's font size to a slider's value. So to look at how this was done, we need to first select the textbox, then find the palette that has the Fontsize property (it's in the text palette). Then we right click over the font size and get the databinding menu to appear.
Selecting DataBinding loads a screen which allows us to pick the binding provider and its field.
That's it; now anytime the slider value is changed, the textbox's font size will also change. See this is action:

Alpha Shading on Textboxes

This is not really about textboxes, but more about alpha shading. I am just using a TextBox because it's easy to do. To do this, we simply use the appearance palette to change the opacity value; 100% is totally visible, 0% is totally invisible.
This is used to produce these nice effects on the demo application.
You can see that the "Welcome.wmv" text is semi-transparent, which is nice.

Animations of Button on Mouse-overs

Ah, now to animations, which I quite like. To do this, we need to consider a couple of things: timelines, transforms, and timeline properties.
First, let me tell you what animations are and how they work at a conceptual level.
An animation could be moving, resizing, changing color, rotating, etc., of an object; these changes occur in time, and need to be told when and how to start the animation process.
Let's look at this a bit further.
  1. To create an animation sequence in time, we need to create a timeline
  2. To change the object (button in this case), we need to transform it somehow
  3. To start an animation, we need to trigger it somehow
So how do we do this in Blend?
Well firstly, we create a new timeline; for example, I have created a new timeline called "tlBtnPlay" for the Buttonobject "btnPlay", which is shown below. What we do is use the "Create New Timeline button". Can you also see that the highlighted timeline is marked as "tlBtnPlay"? This lets us know what timeline we are editing.
It can be seen that this contains frames 0-4. Frame 2 and frame 4 have some strange dots on them. These are called key frames. In my example, at frame2, I flip the button object vertically (I did this flipping using the Transform palette).
This means that whenever the animation is run, when it gets to frame 2, the button object will be flipped. That's all cool. But how do we trigger the animation then?
Well, we use the "Triggers" palette, and create a new "Event Trigger" using the button provided. Then, we can pick the control's property, such as mouseDown etc.
So this creates a trigger, but how do we then get the trigger to run some animation (a timeline basically)? Well, we use the "Add New Value" button.
From here, we can use the dropdown lists to select the timeline to play (first dropdown), and the state of it (second dropdown), where the state is one of the following: BeginStopPauseSkipToFillResume, or Remove. I have used Begin, to state that the animation should begin when the trigger is raised.
So in runtime, if I mouse over a button, we will see the animation happen, as shown below.
It can be seen that a tooltip is shown. This is because this is still simply a .NET 3.0 Button control, but it's been re-styled through custom templating, and has animation applied to it. Isn't that cool? I think so.

Animations of Text on Mouse-overs

I also use some animation (done in exactly the same way) for some text at the bottom right of the demo app.

Playing of Multimedia of the User's Choice

In the demo application, the user may choose some multimedia to play, and provided it's a file, I let them play, and if it's not too large, the media is played. This is all achieved on one of the button click events. This used to be a piece of cake in Expression Interactive Designer, but I can not find such a method of wiring up control events in Expression Blend.
There used to be an Events palette (in Expression Interactive Designer, which is what I used to create this first draft of this article) to bind the control's event to a method name. Mmmm, perhaps there is something I am missing, but for now, let's just carry on and see how we can deal with the multimedia item. If I find out, or someone tells me about the events problem, I will amend this article again.
Then to do the multimedia work, it is achieved in 6 lines of code. Well, neat.
The code to do this is basically as follows:
player = new MediaPlayer();
player.Open(new Uri(@mediaItem, UriKind.Relative));
VideoDrawing aVideoDrawing = new VideoDrawing();
aVideoDrawing.Rect = new Rect(60,60,imgMM.Height,imgMM.Width);
aVideoDrawing.Player = player;
player.Play();

4. Visual Studio 2005 Integration

After I had the C# project in Visual Studio, I added the code-behind logic. Although Expression Blend does actually have a code editing pane, it's not nearly as responsive/nice to use as within Visual Studio. I did experiment with the code editing in Expression Blend, but did not find a way to debug, whilst in Visual Studio, we know we can simply set a break point and then debug a project as normal.
The other funny thing that happened in Expression Blend was that I wanted to reference some .NET 2.0 DLLs and it didn't like that. I suppose that fair enough, as Expression Blend is really built to target the .NET 3.0 Framework. This did, however, frustrate me, as I wanted to be able to try out ideas in Blend, but couldn't as it wouldn't let me reference the relevant DLLs. As I mentioned earlier, I was using a CTP version of Blend (which was previously called Expression Interactive Designer), so perhaps this has all changed in the BETA/final version.
Let's hope so.

5. The Code

OK, so let's see all the code for the attached demo application.

Scene1.Xaml

To be honest, it simply is not worth putting up all the XAML, as there are 100's of lines of it. The best place to look for this is within the demo application itself.

Scene1.Xaml.cs

using System;
using System.IO;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Navigation;
using System.Windows.Media.Imaging;
using System.Windows.Forms;


namespace XAML_MMedia
{
public partial class Scene1
{
String mediaItem ="";
String mediaItemShortName = "
";
String imgLogo="
";
String mediaType="
";
mediaChecker mc = new mediaChecker();
double oldHeight = 0;
double oldWidth = 0;
bool fetchHeights = false;
MediaPlayer player;

public Scene1()
{
this.InitializeComponent();
}

private void getNewMediaItem(object sender,
System.Windows.RoutedEventArgs e)
{
try {
System.Windows.Forms.OpenFileDialog ofd =
new System.Windows.Forms.OpenFileDialog();
if (ofd.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
if (!ofd.FileName.Equals(String.Empty)) {
FileInfo fi = new FileInfo(ofd.FileName);
mediaChecker.selFileType fileType =
mc.getFileType(fi.Extension);
if (!fileType.Equals(
mediaChecker.selFileType.Neither))
{
double fSize = (double)fi.Length / 1024;
if (fSize < 10000)
{
mediaType = fileType.ToString();
if (fileType.Equals(
mediaChecker.selFileType.Audio))
{
imgLogo = "
musicLogo.png";
}
else
{
imgLogo = "
videoLogo.png";
}
mediaItem = fi.FullName;
mediaItemShortName = fi.Name;
}
else
{
System.Windows.MessageBox.Show("
The file" +
"
you picked is too large\r\n" +
"
The limit is 10 MBytes", "Error",
System.Windows.MessageBoxButton.OK,
System.Windows.MessageBoxImage.Error);
}
}
else
{
System.Windows.MessageBox.Show("
You pick " +
"
an unsupported file", "Error",
System.Windows.MessageBoxButton.OK,
System.Windows.MessageBoxImage.Error);
}
}
else {
System.Windows.MessageBox.Show("
You need to" +
"
select a file", "Error",
System.Windows.MessageBoxButton.OK,
System.Windows.MessageBoxImage.Error);
}
}
else
{
System.Windows.MessageBox.Show("
You need to" +
"
select a file", "Error",
System.Windows.MessageBoxButton.OK,
System.Windows.MessageBoxImage.Error);
}
}
catch (Exception ex) {
System.Windows.MessageBox.Show(ex.Message,
"
An error occurred",
System.Windows.MessageBoxButton.OK,
System.Windows.MessageBoxImage.Error);
}
}


private void playMedia(object sender,
System.Windows.RoutedEventArgs e)
{
try {
if (mediaItem.Equals(String.Empty)) {
return;
}
else {
player = new MediaPlayer();
player.Open(new Uri(@mediaItem, UriKind.Relative));
VideoDrawing aVideoDrawing = new VideoDrawing();
aVideoDrawing.Rect = new Rect(60,60,
imgMM.Height,imgMM.Width);
aVideoDrawing.Player = player;
player.Play();
// create a drawing image to host the VideoDrawing
DrawingImage di = new DrawingImage(aVideoDrawing);
// freeze DrawingImage for performance
di.Freeze();
// assign DrawingImage as source of the image
imgMM.Source = di;
if (mediaType.ToLower().Equals("
audio"))
{
if (!fetchHeights)
{
oldWidth = imgMM.Width;
oldHeight = imgMM.Height;
fetchHeights = true;
imgMM.Width = 0;
imgMM.Height = 0;
}
}
else if (mediaType.ToLower().Equals("
video"))
{
if (fetchHeights)
{
imgMM.Width = oldWidth;
imgMM.Height = oldHeight;
}
}
Uri uri = new Uri(@imgLogo, UriKind.Relative);
BitmapImage bitmap = new BitmapImage(uri);
imgMediaType.Source = bitmap;
txtMediaType1.Text = mediaItemShortName;
txtMediaType2.Text = "
Thats an " + mediaType +
"
media item";
}
}
catch (Exception ex) {
System.Windows.MessageBox.Show(ex.Message,
"
An error occurred",
System.Windows.MessageBoxButton.OK,
System.Windows.MessageBoxImage.Error);
}
}
}
}
There is also a very simple helper class called mediaChecker,which is as follows:
using System;
using System.Collections.Generic;
using System.Text;
using System.Collections;

namespace XAML_MMedia
{
public class mediaChecker
{
public enum selFileType { Audio,Video, Neither }
private List<string> allowedVideoExtensions =
new List<string>();
private List<string> allowedAudioExtensions =
new List<string>();

public mediaChecker()
{
allowedVideoExtensions.Add(".wmv");
allowedAudioExtensions.Add(".wma");
allowedAudioExtensions.Add(".mp3");
}

public selFileType getFileType(String extension)
{
if (allowedAudioExtensions.Contains(extension))
{
return selFileType.Audio;
}
else if (allowedVideoExtensions.Contains(extension))
{
return selFileType.Video;
}
return selFileType.Neither;

}
}
}