Av rating:
Total votes: 17
Total comments: 3


Jesse Liberty and Alex Horovitz
Getting Started with XAML
16 March 2007

Creating .NET 3 Applications with XAML

Microsoft's .NET 3 introduces the option of using markup to create Windows applications, much as we've used HTML and related markup to create web applications for the past decade. This is a major break-through and a distinct step on the path towards merging the development process of creating web and desktop applications.

This new markup language is called eXtensible Application Markup Language, or XAML (pronounced /'zæməl/ to rhyme with camel). Every XAML object represents a CLR object, and every CLR object may be created declaratively in XAML.

In this article we'll create a meaningful, working XAML application. We'll take you through the creation process, with the goal of demonstrating the flexibility of the language and acquainting you with the fundamentals of creating a working Windows application declaratively.

Getting Started With XAML

Every Window is declared as a panel and every property is declared as an element, or an attribute of an element. In its simplest form, this looks very similar to HTML:

<Button Width="20" Height="10">OK Button</Button>

In fact, XAML is an XML-based superset of HTML, combining the power of HTML to define content, with that of CSS to define layout (carefully separating the two, to the relief of experienced CSS programmers).

Note: To follow along with the examples in this article, you will need to install the release version of .NET 3, which will run happily and harmlessly side-by-side with .NET 2 on any computer running Vista, Windows XP (SP2), or Windows Server 2003 SP1.

Core XAML Elements

While we won't even try to cover everything in XAML in this article, we will cover some core elements, both to give you a feel for how XAML applications work and to lay the ground work for walking you through a robust sample application.

A panel element is a container. Panels come in several different shapes and sizes. Depending on their features they are useful for containing other elements.

Root elements are special derivations of panel elements and serve as the fundamental container for the page. Each page will have exactly one root element.

Control elements are user-manipulated objects that help with data or user interactions.

The PelotonShack Project

With these elements, we are going to build a data-bound page that will offer a catalog of bicycle parts from the AdventureWorks2000 database. On the left, there will be a scrolling display of images taken from the database. As the user scrolls over each image, it (that is, the image, not the user) will grow slightly and come into focus. If the user clicks on the image, it will be highlighted in the larger area on the right and its details will be displayed in text, as shown in Figure 1.

 

Figure 1: in which the selected image (not the user) is shown in detail in the right hand panel

Creating the Web Application

To create this application, begin by opening Visual Studio and creating a new Project. Choose the Windows Application (WPF) template, set your directory and set the solution name to PelotonShack, as shown in Figure 2.

Figure 2: creating the web application

Windows will create a development environment and establish your first window, which it will also name for you:

<Window x:Class="PelotonShack.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="PelotonShack" Height="300" Width="300">
    <Grid>
        ...
    </Grid>
</Window>

While there will certainly come a time (and soon) when Visual Studio will offer a complete and working toolkit for building WPF applications, for now we're going to have the most success and least frustration if we write directly in XAML, in the lower markup window.

Start by setting the height of Window1 to 600 and the width to 900 (you can fine-tune these numbers later).

Within the Grid, we'll want to add Grid.Resources (to manage styles, GradientBrushes, etc.) and we'll want to add column definitions.

The Header

An easy place to start is by adding the header, as shown in Figure 3:

Figure 3: adding the header

To create this, we'll use a text block and style it. The text block is pretty straightforward:

<TextBlock Style="{DynamicResource TitleText}">
   <Span>The Peloton Shack: </Span>
   <Span FontStyle="Italic"> keeping you in the pack!</Span>
</TextBlock>

Notice, however, that the TextBlock element is marked with a style reference to the DynamicResource 'TitleText'. Resources are separated out in their own section, so the complete XAML at this point looks as shown in example 1:

                          Example 1
<Window x:Class="PelotonShack.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="PelotonShack" Height="600" Width="900">
    <Grid>
       <Grid.Resources>
       <Style x:Key="TitleText" TargetType="{x:Type TextBlock}" >
          <Setter Property="FontFamily" Value="Segoe Black" />
          <Setter Property="FontSize" Value="24px" />
          <Setter Property="Foreground" Value="MidnightBlue" />
       </Style>
       </Grid.Resources>
      
       <TextBlock Style="{DynamicResource TitleText}">
          <Span>The Peloton Shack: </Span>
          <Span FontStyle="Italic"> keeping you in the pack!</Span>
       </TextBlock>
    </Grid>
</Window> 

From such humble beginnings, all the rest will follow in just a few easy steps.

 Adding the List Box

To add the list box of products, we need to accomplish a few tasks all at the same time.

1. Add a panel to hold the list box in a specific place in our window.

2. Add a list box to hold the images.

3. Include style information to tell the list box how to render the data (as images).

4. Bind the list box to a query of the images from the database.

The panel we'll use to hold the list box will, ultimately, have the list box on the left, the larger image on the right and text below. That is, it needs to have two columns and a row. A stack panel can hold items one above the other or one next to another, but when you need to have both rows and columns, the panel you need is a grid:

<Grid x:Name="MainGrid" Margin="125,99,127,118" Width="Auto" 
          Height="Auto" RenderTransformOrigin="0.5,0.5">
    <Grid.ColumnDefinitions>
       <ColumnDefinition Width="0.33*"/>
       <ColumnDefinition Width="0.66*"/>
    </Grid.ColumnDefinitions>
    <Grid.RowDefinitions>
       <RowDefinition/>
    </Grid.RowDefinitions>
    <ContentControl x:Name="MasterPane" HorizontalAlignment="Center"
            VerticalAlignment="Stretch" RenderTransformOrigin="0.5,0.5">
       <ListBox x:Name="MasterList"
          Width="Auto" Height="Auto"
          RenderTransformOrigin="0.5,0.5"
          ItemsSource="{Binding Mode=Default}"
       ItemTemplate="{DynamicResource ProductDataTemplate}"
       ItemContainerStyle="{DynamicResource ProductTemplateItemStyle}"
       HorizontalAlignment="Center"
          SelectedIndex="0"
          IsSynchronizedWithCurrentItem="True"
       ScrollViewer.VerticalScrollBarVisibility="Visible"
    >
       </ListBox>
    </ContentControl>
    <ContentControl  x:Name="DetailsPane" Grid.Column="1" />
 </Grid>

The first two lines set up the grid's attributes (margin, width, etc.). This is followed by the Grid column definitions, in which we define two columns: one will occupy 1/3 of the grid's width, while the second will occupy the remaining 2/3 of the grid's width. One row is defined in the Row definitions.

Following the Row and Column definitions are two content controls: MasterPane and DetailsPane. The second content control, DetailsPane, is left empty for now. The first content control holds the list box. The key attribute for the list box is that the ItemSource is set to bind using the default mode. Since this is placed within a window where the DataContext was set to 'theTable', the list box will use 'theTable' as its binding context. The style for binding is set by the ItemTemplate.

ItemsSource="{Binding Mode=Default}" 
ItemTemplate="{DynamicResource ProductDataTemplate}"
The ProductDataTemplate must be defined up in the Grid.Resources section.
<DataTemplate x:Key="ProductDataTemplate">
   <StackPanel HorizontalAlignment="Center"
      Background="{DynamicResource ListBoxGradient}" 
      Height="Auto" Width="150">
      <Image Source="{Binding Path=ThumbNailPhotoFilePath}"
             ContextMenuService.IsEnabled="True"
         ContextMenuService.ContextMenu="{Binding Path=Name}"
         HorizontalAlignment="Center" />
   </StackPanel>
</DataTemplate>

It consists of a stack panel whose horizontal alignment is set to center the items and whose image will be retrieved from the binding where the path is set to ThumbNailPhotoFilePath . This is a column that will be retrieved for every row as a result of the select statement that will generate the DataTable we'll be using as the DataContext for this list box.

All of the work for generating the DataTable, and associating it with the context, is accomplished in the code-behind file, the constructor for the Window class:

public partial class Window1 : System.Windows.Window
{
   DataTable theTable = new DataTable();
   public Window1()
   {
      InitializeComponent();
      String connString = @"Data Source=<Your Server>;Initial
         Catalog=AdventureWorks2000;Integrated Security=True";
      String query = @"SELECT 
                             Product.ProductID,
                             Product.Name,
                             Product.ProductNumber,
                             Product.ListPrice,
                             Product.Color,
                             Product.Size,
                             Product.Style,
                             ProductPhoto.ThumbNailPhotoFilePath,
                             ProductPhoto.LargePhotoFilePath 
                           FROM        
                             Product
                           INNER JOIN
                             ProductPhoto
                           ON
                             Product.ProductPhotoID = ProductPhoto.ProductPhotoID
                           WHERE    
                             (Product.ProductPhotoID = ProductPhoto.ProductPhotoID
                                 AND Product.ListPrice < 100)";
      using ( SqlConnection conn = new SqlConnection( connString ) )
      {
         SqlDataAdapter da = new SqlDataAdapter( query, conn );
         da.Fill( theTable );
      }
      DataContext = theTable;
   } 

The net result is a scrollable list of images extracted from the database, as demonstrated in Figure 4:

Figure 4: a scrollable list of images

Adding Events

We'd like to be able to mouse over the list and make the image currently highlighted by the mouse stand out more than the others. We'll do this by reducing the opacity of the images to 60% and then increasing that to 100% when the mouse hovers over the image. To accomplish this effect, we add animation, using a story board.

We'll indicate that the starting opacity is .60 (60%); and we’ll designate that over the course of two tenths of a second that opacity should increase to 100% when the event MouseEnter is registered. This opacity will also return to 60% when the MouseLeave event is raised. We declare these requirements by creating a ProductTemplateItemStyle for the ItemContainer and then creating that style within the Resources section:

<Style x:Key="ProductTemplateItemStyle" TargetType="{x:Type ListBoxItem}">
   <Setter Property="Opacity" Value=".60" />
   <Style.Triggers>
      <EventTrigger RoutedEvent="Mouse.MouseEnter">
         <EventTrigger.Actions>
            <BeginStoryboard>
               <Storyboard>
                  <DoubleAnimation
                     Duration="0:0:0.2"
                     Storyboard.TargetProperty="Opacity"
                     To="1.0" />
               </Storyboard>
            </BeginStoryboard>
         </EventTrigger.Actions>
      </EventTrigger>
      <EventTrigger RoutedEvent="Mouse.MouseLeave">
         <EventTrigger.Actions>
            <BeginStoryboard>
               <Storyboard>
                  <DoubleAnimation
                     Duration="0:0:0.2"
                     Storyboard.TargetProperty="Opacity" />
               </Storyboard>
            </BeginStoryboard>
         </EventTrigger.Actions>
      </EventTrigger>
   </Style.Triggers>
</Style>

The effect is powerful: As you mouse over each item, it appears to light up.

Creating the Detail View

We can now add the second container control to hold the larger image of the selected item and its description. The most interesting thing we'll do here is add a 'reflection':

<ContentControl x:Name="DetailsPane" Grid.Column="1" 
        Margin="15,0,0,0" Width="Auto" Height="Auto"
   RenderTransformOrigin="0.5,0.5">
 <Grid x:Name="DetailsGrid" Width="Auto" Height="Auto"
         RenderTransformOrigin="0.5,0.5">
  <Grid.ColumnDefinitions>
   <ColumnDefinition/>
  </Grid.ColumnDefinitions>
  <Grid.RowDefinitions>
   <RowDefinition/>
  </Grid.RowDefinitions>
  <StackPanel>
   <Image x:Name="ProductImage"
            RenderTransformOrigin="0.5,0.5" 
            Width="Auto" Height="200" Source="{Binding Path=LargePhotoFilePath}"/>
   <Rectangle  Fill="{DynamicResource ReflectionBrush}"
               Margin="0,0,0,0" Stroke="{x:Null}"
            Width="{Binding Path=ActualWidth,
               ElementName=ProductImage, Mode=Default}"
            Height="200" RenderTransformOrigin="0.5,0.5">
    <Rectangle.LayoutTransform>
     <TransformGroup>
      <ScaleTransform ScaleX="1" ScaleY="-1"/>
      <SkewTransform AngleX="0" AngleY="0"/>
      <RotateTransform Angle="0"/>
      <TranslateTransform X="0" Y="0"/>
     </TransformGroup>
    </Rectangle.LayoutTransform>
    <Rectangle.OpacityMask>
     <LinearGradientBrush StartPoint="0.5,1.5"
                             EndPoint="0.5,-0.5">
      <GradientStop Color="#7FFFFFFF" Offset="0.21"/>
      <GradientStop Color="#00FFFFFF" Offset="0.392"/>
     </LinearGradientBrush>
    </Rectangle.OpacityMask>
   </Rectangle>
  </StackPanel>
 <TextBlock x:Name="Name" Margin="0,0,2,19" HorizontalAlignment="Left"
    VerticalAlignment="Bottom" Width="64" Height="26"
      RenderTransformOrigin="0.5,0.5" TextWrapping="Wrap"
      Text="{Binding Path=Name}" TextAlignment="Left"/>
 <TextBlock x:Name="ProductNumber" Margin="0,0,2,19"
    HorizontalAlignment="Center" VerticalAlignment="Bottom"
      Width="64" Height="26" RenderTransformOrigin="0.5,0.5"
      TextAlignment="Center"  Text="{Binding Path=ProductNumber}"/>
 <TextBlock x:Name="ListPrice" Margin="0,0,2,19"
    HorizontalAlignment="Right" VerticalAlignment="Bottom" Width="64"
      Height="26" RenderTransformOrigin="0.5,0.5"
      TextAlignment="Right" Text="{Binding Path=ListPrice,
      Converter={StaticResource myNumberFormatter},
      ConverterParameter='$#,###.00'}"/>
 </Grid>
</ContentControl>

Creating the Reflection

The reflection is created by adding a grid within the content control. Within that grid we will add a stack panel. The image is stacked on top of a rectangle that uses a LayoutTransform to flip the image upside down (ScaleY = -1). The rectangle then uses an OpacityMask with a LinearGradientBrush that starts out at 0.5,1.5 and ends at 0.5, -0.5; that is, the image appears to fade away quickly:

<Rectangle.LayoutTransform>
  <TransformGroup>
   <ScaleTransform ScaleX="1" ScaleY="-1"/>
   <SkewTransform AngleX="0" AngleY="0"/>
   <RotateTransform Angle="0"/>
   <TranslateTransform X="0" Y="0"/>
  </TransformGroup>
</Rectangle.LayoutTransform>
<Rectangle.OpacityMask>
  <LinearGradientBrush StartPoint="0.5,1.5" EndPoint="0.5,-0.5">
   <GradientStop Color="#7FFFFFFF" Offset="0.21"/>
   <GradientStop Color="#00FFFFFF" Offset="0.392"/>
  </LinearGradientBrush>
</Rectangle.OpacityMask>

The effect can be seen in this close-up of the rendered image in Figure 5:

Figure 5: reflection

To finish up, we'll add a GridSplitter, telling it which column we want to split. This will allow the user to reallocate the relative space between the list box and the details:

<GridSplitter x:Name="GridSplitter1" 
      Grid.Column="1" Margin="1.5,1.5,0,3"
   HorizontalAlignment="Left" Width="11.5"
   Height="Auto" RenderTransformOrigin="0.5,0.5"/>

The effect is shown in Figure 6:

Figure 6: the gridsplitter

Complete Source

The complete source can be downloaded with this article. You can also visit http://www.gotdotnet3.com/ where a free support forum and related support material are available. The source can be run on any machine with the .NET 3 Framework and SDK installed.



This article has been viewed 10208 times.
Jesse Liberty and Alex Horovitz

Author profile: Jesse Liberty and Alex Horovitz

Jesse Liberty and Alex Horovitz are the authors of Programming .NET 3.0 , now available in Rough Cuts version from O'Reilly. Jesse Liberty, Microsoft .NET MVP, is the best-selling author of O'Reilly Media's Programming ASP.NET, Programming C#, the forthcoming Learning ASP.NET/AJAX and over a dozen other books on programming. He is president of Liberty Associates, Inc where he provides contract programming, and consulting and he is a frequent contributor to Simple-Talk. Jesse maintains a political blog a technical blog , and a free private forum on which he provides support for all his writing. Alex Horovitz spent time at both NeXT Computer and Apple in the 1990s. Currently he provides software engineering leadership and programming to clients seeking to develop enterprise applications leveraging the Model-View-Controller design pattern and re-usable Frameworks.

Search for other articles by Jesse Liberty and Alex Horovitz

Rate this article:   Avg rating: from a total of 17 votes.


Poor

OK

Good

Great

Must read
 
Have Your Say
Do you have an opinion on this article? Then add your comment below:
You must be logged in to post to this forum

Click here to log in.


Subject: Fantastic
Posted by: Krishnan (not signed in)
Posted on: Friday, June 22, 2007 at 8:04 AM
Message: This is great piece of innovation. .NET rocks

Subject: can we increase the length of Column
Posted by: Gowtham Papolu (not signed in)
Posted on: Thursday, July 26, 2007 at 7:49 AM
Message: Hi,

Can u please Help me in increasing the length of Column of datatable within the datagrid in Windows Presentation Form....

Regards,
Gowtham.

Subject: XAMl templates
Posted by: Anonymous (not signed in)
Posted on: Tuesday, February 26, 2008 at 12:01 PM
Message: At http://www.xamltemplates.net you can download a complete theme made in xaml for all the controls in WPF and also there are a lot of themes to buy.

 






recommended site pinvoke

PInvoke.net is a user-driven wiki which provides .NET developers with native method signatures, so they don't have to spend time writing them from scratch.





Damon Armstrong
Customizing the Login Page in SharePoint 2007
 Damon shows how a few simple steps lead you to being able to include the login form in a consistent look and feel to...  Read more...


ANTS Profiler and the Un-Rest Cure
 After a while, successful applications can get set in their ways. Bart Read and Andrew Hunter decided... Read more...

Silverlight-Speed Loop
 John Bower steps up a gear, produces a Lamborghini, and examines the process of using a high-speed... Read more...

Sid: Vicious
 Dan Archer documents his epic struggle with an apparently simple task of authenticating user... Read more...

Embedding Help so it will be used
 It is not good enough to make assumptions about the way that users go about getting help when they use... Read more...

Optimising a High-Performance Computing Tool
 Many computer systems nowadays have their ‘correctness’ checked using sample testing, but this isn't... Read more...

A Complete URL Rewriting Solution for ASP.NET 2.0
 Ever wondered whether it's possible to create neater URLS, free of bulky Query String parameters?... Read more...

.NET Application Architecture: the Data Access Layer
 Find out how to design a robust data access layer for your .NET applications. Read more...

Web Parts in ASP.NET 2.0
 Most Web Parts implementations allow users to create a single portal page where they can personalize... Read more...

Visual Studio Setup - projects and custom actions
 This article describes the kinds of custom actions that can be used in your Visual Studio setup project. Read more...

Beginning ASP.NET 2.0
 It seems that there is both excitement and confusion surrounding Master Pages and Themes. A big part of... Read more...

Over 150,000 Microsoft professionals subscribe to the Simple-Talk technical journal. Join today, it's fast, simple, free and secure.

Join Simple Talk