C# and WPF

So far, just about everything you’ve done with WPF has been declarative; that is, all the functionality has taken place in the XAML file. WPF is specifically designed that way, to be useful to designers as well as to developers. The only C# you’ve had to write so far has been some very rudimentary event handlers. In this section you’re going to create an example that more closely resembles a production application, and that’s going to involve a supporting class, and some event handlers.

In this example, you’re going to grab the images of the first 20 presidents of the United States from the White House’s website, and present them in a custom WPF control, a modified ListBox control. The control will not be wide enough to show all 20 images, so you’ll provide a horizontal scroll bar, and as the user moves the mouse over an image, you’ll provide feedback by enlarging that image (from 75 to 85) and increasing its opacity from 75% to 100%. As the user moves the mouse off the image, you’ll return the image to its smaller, dimmer starting point.

This application will use some declarative animation, as you’ve already seen, although slightly subtler than the rotating square. In addition, when the user clicks on an image, you’ll handle the click and display the name of the president using a C# event handler, and then you’ll reach into the control and place the president’s name into the title bar of the control.

Figure 19-7 shows the result of scrolling to the 16th president and clicking on the image. Note that the name of the president is displayed in the title bar and that the image of President Lincoln is both larger and brighter than the surrounding images.

The Presidential Browser application makes use of some slightly subtler animations, but most of it still takes place in the XAML.

Figure 19-7. The Presidential Browser application makes use of some slightly subtler animations, but most of it still takes place in the XAML.

Grids and Stack Panels

Create a new WPF application called Presidential Browser. Up until now, you’ve placed all your elements in the default Grid control that WPF provides. This time, though, you want two items in the grid—the text block that says “United States Presidents” and the sideways ListBox of photographs, so you can make use of WPF’s layout elements.

In addition to the grid element, WPF provides a layout object called a stack panel. A stack panel lets you stack a set of objects one on top of (or next to) another set of objects. That turns out to be very useful for laying out your page. If you want a stack that is horizontal and vertical (essentially a table), that’s what the grid element is for. A grid has columns and rows, both counting from zero.

You’ll create a simple grid of two rows and one column, and inside each row you’ll place a stack panel. The top stack panel will hold the text, and the bottom stack panel will hold the ListBox that will, in turn, hold the photos. We’ll break this down for you and take it one step at a time.

To begin, set the width of the Window element to 330 and the height to 230. Next, give the grid some dimensions, by adding properties to the grid element. A width of 300 and a height of 190 should do it. Add the properties like this:

<Grid Width="300" Height="190" >

Next, you’ll need to define the rows in the grid element. That’s easy enough to do with the RowDefinition element, but you’ll need to put that within a <Grid.RowDefinitions> element, like this:

<Grid Width="300" Height="190">
    <Grid.RowDefinitions>
        <RowDefinition Height="20" />
        <RowDefinition Height="*" />
    </Grid.RowDefinitions>

You know that you want the first row to be a fixed 20 units high, so that number’s hardcoded. The second row, though, should be whatever space is left in the grid. You could do the math yourself (and come up with 170), but the * element lets WPF do it for you.

The next things you need to add to the Grid are the two StackPanel elements. These are relatively easy: you just add the StackPanel elements inside the <Grid> tags. Inside each StackPanel, you’ll add a TextBlock element, which does what it sounds like—it holds text. The TextBlock is a flexible control for displaying text, but here we’re just using it to align the text in the panel. Add the following code to the XAML:

<StackPanel Grid.Row="0">
    <TextBlock FontSize="14">United States Presidents
    </TextBlock>
</StackPanel>
<StackPanel Grid.Row="1">
    <TextBlock Text="Bottom Stack Panel" VerticalAlignment="Center"/>
</StackPanel>

The first thing you need to notice here is that the rows in the grid are numbered from zero, the same as with arrays. The TextBlock element has a property for FontSize; it also has font weight and font family and the other features you might expect in a text element.

Your XAML code should now look like Example 19-6.

Example 19-6. This is the starting XAML for the Presidential Browser application, with the layout elements in place

<Window x:Class="Presidential_Browser.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1" Height="300" Width="300">
    <Grid Width="300" Height="190">
        <Grid.RowDefinitions>
            <RowDefinition Height="20" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>
        <StackPanel Grid.Row="0">
            <TextBlock Text="Top Stack Panel" VerticalAlignment="Center"/>
        </StackPanel>
        <StackPanel Grid.Row="1">
            <TextBlock Text="Bottom Stack Panel" VerticalAlignment="Center"/>
        </StackPanel>
    </Grid>
</Window>

Defining ListBox styles

Your next goal is to get the pictures into a ListBox and to turn the ListBox sideways so that the pictures scroll along, as shown previously in Figure 19-7.

To accomplish that, we need to do two things: we need to work with the style of the ListBox, and we need to work with its data. We’ll take these one at a time to make it clear.

You’ve created a Brush as a resource before; now you’ll make one for the entire Window element, in a Window.Resources section. This brush will be a LinearGradientBrush, which is a bit fancier than the Fill brush you used before. The gradient brush uses a nice shading effect that changes gradually through the colors identified in the GradientStop elements. The exact details aren’t important, but you should note that we’re giving this resource a name, as all resources have, so we can use it on the ListBox later. Create a Window.Resources section at the top of your XAML file, and add this code:

<Window.Resources>
    <LinearGradientBrush x:Key="ListBoxGradient"
           StartPoint="0,0" EndPoint="0,1">
        <GradientStop Color="#90000000" Offset="0" />
        <GradientStop Color="#40000000" Offset="0.005" />
        <GradientStop Color="#10000000" Offset="0.04" />
        <GradientStop Color="#20000000" Offset="0.945" />
        <GradientStop Color="#60FFFFFF" Offset="1" />
    </LinearGradientBrush>
</Window.Resources>

Briefly, all linear gradients are considered as occurring on a line ranging from 0 to 1. You can set the start points and endpoints (traditionally, the start point 0,0 is the upper-left corner and the endpoint 1,1 is the lower-right corner, making the linear gradient run on an angle). We’ve set the linear gradient to end at 0,1, making the gradient run from top to bottom, giving a horizontal gradient, moving through five colors, unevenly spaced.

The next resource you need to define is a Style object. We haven’t specifically applied a style as a resource yet, but Style objects work like any other resource: they manage the properties of their targets, in this case, their style properties.

A difference in this case is that instead of defining a TargetName for the resource, you’ll define a TargetType. That means that the style will be applied to all objects of a specific type (in this case, ListBox). Within the Style, you define a Template, which means that the style definition can be applied to objects, or modified by them. Within that, there’s a set of properties defined for the Border element, most of which are pretty self-explanatory. Notice that for a background, the Border element uses the ListBoxGradient brush that you defined a moment ago.

Within the Border element is a ScrollViewer element. This element is what gives the ListBox a horizontal scroll bar, but not a vertical one. Within the ScrollViewer is another StackPanel element—this is where you’ll keep the images of the presidents. The IsItemsHost property indicates that this StackPanel will hold other objects (you’ll see how this works in a bit), and the Orientation and HorizontalAlignment properties simply orient the StackPanel inside the ScrollViewer.

Add the following Style within the Window.Resources element, right after the LinearGradiantBrush:

<Style x:Key="SpecialListStyle" TargetType="{x:Type ListBox}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type ListBox}" >
                <Border BorderBrush="Gray"
                     BorderThickness="1"
                     CornerRadius="6"
                     Background="{DynamicResource ListBoxGradient}" >
                     <ScrollViewer 
                         VerticalScrollBarVisibility="Disabled"
                         HorizontalScrollBarVisibility="Visible">
                            <StackPanel IsItemsHost="True"
                            Orientation="Horizontal"
                            HorizontalAlignment="Left" />
                     </ScrollViewer>
                </Border>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

Triggers and animations

The style for the ListBox that you just created contains a StackPanel that contains items. The next thing you’ll do is define the style for these items. The items, you’ll recall, are the images of the presidents that you’re displaying. We mentioned that these images will change appearance when the user moves the mouse over them. You saw earlier in the chapter how to interact with the user’s mouse movements—you’ll need to define some triggers. The Triggers will reside in the Style, rather than being attached to a specific instance of a control.

This style begins by setting its target type, as the last style did (ListBoxItems), and three properties: MaxHeight, MinHeight, and Opacity. The MaxHeight and MinHeight properties have the same value, which means that the size of the items is fixed, but you’ll be able to change that dynamically, as you’ll see shortly. The Opacity of a control is defined as a value between 0 and 1:

<Style x:Key="SpecialListItem"
    TargetType="{x:Type ListBoxItem}">
        <Setter Property="MaxHeight"  Value="75" />
        <Setter Property="MinHeight"  Value="75" />
        <Setter Property="Opacity"    Value=".75" />

The style then sets a couple of triggers. As with the triggers you saw earlier in the chapter, these triggers associate an EventTrigger with a RoutedEvent. Specifically, the first trigger uses the MouseEnter event that you saw in an earlier example:

<EventTrigger RoutedEvent="Mouse.MouseEnter">

In this case, the event will be kicked off when the mouse enters the object that is associated with this EventTrigger (that object will be the ListBox item), as opposed to targeting a specific control as you did earlier in the chapter. Within that EventTrigger you defined an EventTrigger.Actions element. In this case, the action is BeginStoryBoard, and there is a single, unnamed Storyboard:

<EventTrigger RoutedEvent="Mouse.MouseEnter">
    <EventTrigger.Actions>
        <BeginStoryboard>
            <Storyboard>
                <DoubleAnimation Duration="0:0:0.2"
                    Storyboard.TargetProperty="MaxHeight"  To="85" />
                <DoubleAnimation Duration="0:0:0.2"
                    Storyboard.TargetProperty="Opacity" To="1.0" />
            </Storyboard>
        </BeginStoryboard>
    </EventTrigger.Actions>
</EventTrigger>

The action is inside the storyboard, where you’ll find two animations. These are DoubleAnimation elements, because you’re changing two properties with double values. These two animations are defined to have a duration of 2/10 of a second. The TargetProperty refers to the property of the object to be animated (that is, the ListBox item)—in the first case, the height of the ListBox item, which will be animated to a height of 85 (from a starting point of 75). The second animation will change the opacity from its starting point of .75 to 1 (making it appear to brighten). The other trigger is for the MouseLeave event, and just reverses the effects.

Here’s the XAML for the entire style; add this to the Windows.Resources section:

<Style x:Key="SpecialListItem"
    TargetType="{x:Type ListBoxItem}">
        <Setter Property="MaxHeight"  Value="75" />
        <Setter Property="MinHeight"  Value="75" />
        <Setter Property="Opacity"    Value=".75" />
    <Style.Triggers>
        <EventTrigger RoutedEvent="Mouse.MouseEnter">
            <EventTrigger.Actions>
                <BeginStoryboard>
                    <Storyboard>
                        <DoubleAnimation Duration="0:0:0.2"
                            Storyboard.TargetProperty="MaxHeight" 
                            To="85" />
                        <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="MaxHeight" />
                        <DoubleAnimation Duration="0:0:0.2"
                            Storyboard.TargetProperty="Opacity" />
                    </Storyboard>
                </BeginStoryboard>
            </EventTrigger.Actions>
        </EventTrigger>
    </Style.Triggers>
</Style>

Adding Data

We’re now going to cheat somewhat, and rather than getting our data from a web service or from a database, we’re going to put it right into our resources. The data will consist of a generic list of ImageURL objects. You haven’t heard of these types of objects before, because you’re going to create the class right now. Right-click on the project file in the Solution Explorer and choose Add → Class. When the Add New Item dialog box appears, the Class item will be selected automatically. Leave it selected, and give the class file a name of PhotoCatalog.cs. Visual Studio will automatically open a new class file for you. Add the code in Example 19-7, and be sure to add the using System.Windows.Media.Imaging statement, because you’ll need it, and also be sure to add the public modifier to the class name.

Example 19-7. The ImageURL class defines a new class that you’ll be able to use in the XAML application namespace

using System;
using System.Collections.Generic;
using System.Windows.Media.Imaging;
namespace PhotoCatalog
{
    public class ImageURL
    {
        public string Path { get; private set; }
        public Uri ImageURI { get; set; }

        public BitmapFrame Image { get; private set; }
        public string Name { get;  set; }
        public ImageURL(  ) { }
        public ImageURL(string path, string name)
        {

            Path = path;
            ImageURI = new Uri(Path);
            Image = BitmapFrame.Create(ImageURI);
            Name = name;
        }
        public override string ToString(  )
        {
            return Path;
        }
    }

    public class Images : List<ImageURL> { }
}

You’ve actually created two classes here. The first class, ImageURL, is designed to act as a wrapper —that is, it’ll hold the properties for an image that you retrieve given the path to an image, or a URI from which we can create an image. Note that you override ToString( ) to return the Path property, even though we haven’t explicitly created the backing variable.

The second class is at the very bottom: Images derives from a generic list of ImageURL objects. The implementation is empty—all you’re really doing with this class is providing an alias for List<ImageURL>.

Instantiating objects declaratively

Now that you’ve declared these classes, you can create instances of them in the Resources section of the XAML file—you’re not limited to the types defined by the XAML namespace. However, before you can use your new type, you must first include the class in your XAML file by creating a namespace for this project. At the top of the XAML file, you’ll find the other namespace declarations for this project; they all start with xmlns:. Add your own xmlns statement, and call the new namespace local, like this:

xmlns:local="clr-namespace:Presidential_Browser"

As soon as you type local=, IntelliSense will help you out with the rest of the namespace. You’ll probably notice an error message in the Design window at that point, and you’ll need to reload the designer. Go ahead and do that, and everything will be fine.

Now that you have the local namespace, you can create an instance of the Images class in the Window.Resources section, like this:

<local:Images x:Key="Presidents"></local:Images>

This is the XAML equivalent of writing:

List<ImageURL> Presidents = new List<ImageURL>(  );

You then add to that list by creating instances of ImageURL between the opening and closing tags of the Images declaration:

<local:ImageURL ImageURI="http://www.whitehouse.gov/history/presidents/images/
gw1.gif" Name="George Washington" />

Again, this is the XAML equivalent of writing:

ImageURL newImage = new ImageURL(
     "http://www.whitehouse.gov/history/presidents/images/gw1.gif,"
     "George Washington");
Presidents.Add(newImage);

You’ll need to do that 20 times, once for each of the first 20 presidents. The URL is somewhat long, so you might want to cut and paste it, or you can download the code listing for this chapter from http://www.oreilly.com and cut and paste that part. Now you have a static data resource you can refer to in the rest of your XAML file; and that completes the Resources section.

Using the Data in the XAML

Now that you’ve defined the resource, the next step is to provide a way for the Grid element to access the data in that resource. To do that, provide a DataContext for the Grid:

<Grid Width="300" Height="190"
    DataContext="{StaticResource Presidents}">

Every Framework object has a DataContext object, usually null. If you don’t instruct the object otherwise, it will look up the object hierarchy from where it is defined until it finds an object that does have a DataContext defined, and then it will use that DataContext as its data source. You can use virtually anything as a DataContext—a database, an XML file, or, as in this case, a static resource.

Defining the ListBox

Now that you’ve got all the resources in place, you’re finally ready to define the ListBox and the template for its contents in the second StackPanel. The first thing you need to do is set some of the properties for the ListBox element:

<StackPanel Grid.Row="1" Grid.ColumnSpan="3" >
  <ListBox Style="{StaticResource SpecialListStyle}"
      Name="PresPhotoListBox" Margin="0,0,0,20"
      SelectionChanged="PresPhotoListBox_SelectionChanged"
      ItemsSource="{Binding }"
      IsSynchronizedWithCurrentItem="True" SelectedIndex="0"
      ItemContainerStyle="{StaticResource SpecialListItem}" >

The first line shown here places the stack panel into the grid at row offset 1 (the second row). The ListBox itself has its style set to a StaticResource (the SpecialListStyle resource you defined earlier in the Resources section). The ListBox is named:

Name="PresPhotoListBox"

And an event handler is defined for anytime an image is clicked:

SelectionChanged="PresPhotoListBox_SelectionChanged"

The source for each item is set to Binding, indicating that you’re binding to the source in the parent element (which you just defined in the Grid element’s DataContext property). Finally, the ItemContainerStyle is set, again, to the style defined earlier in the Resources section.

Each item in the ListBox will be drawn from the (unknown) number of items in the data (which in this case happens to be statically placed in the Resources, but could well be dynamically drawn from a web service). To do this, we’ll need a template for how to draw each item. Add the following code as a subelement of the ListBox element:

<ListBox.ItemTemplate>
    <DataTemplate>
        <Border VerticalAlignment="Center"
           HorizontalAlignment="Center" Padding="4"
           Margin="2" Background="White">
            <Image Source="{Binding Path=ImageURI}" />
        </Border>
    </DataTemplate>
</ListBox.ItemTemplate>

Within the ListBox.ItemTemplate, you place a DataTemplate; this is necessary if you want to show anything more than simple text derived from the data retrieved. In this case, you place a Border object within the DataTemplate, and within the Border object you place the Image object. It is the Image object you really care about (though the Border object helps with placement). The Image requires a source; here, you add Binding (indicating that you are binding to the current source), and you state that you’ll be using the ImageURI property to set the Path. Because the source you’re binding to is a list of ImageURL objects, and each ImageURL has four public properties (Path, ImageURI, Image, and Name), this is the critical piece of data required to tell the DataTemplate how to get the information necessary to create the image in the ListBox.

Event Handling

Except for defining the ImageURL class, everything you’ve done so far in this example has been done declaratively, in the XAML file. Now it’s finally time to write some C# in this example. You may have noticed that you did create an event handler for when the user changes the selected item in the ListBox:

SelectionChanged="PresPhotoListBox_SelectionChanged"

This is typically done by clicking on an image (though you can accomplish this with the arrow keys as well). This event will fire the event handler in the code-behind file, which is, finally, C#.

The event handler is, as you would expect, in the code-behind file, Window1.xaml.cs. Switch to that file now, and add the following event handler:

private void PresPhotoListBox_SelectionChanged(
           object sender, SelectionChangedEventArgs e)
{
    ListBox lb = sender as ListBox;
    if (lb != null)
    {
        if (lb.SelectedItem != null)
        {
            string chosenName = 
                 (lb.SelectedItem as ImageURL).Name.ToString(  );
            Title = chosenName;
        
        }
    }
    else
    {
        throw new ArgumentException(
                "Expected ListBox to call selection changed in 
                PresPhotoListBox_SelectionChanged");
    }
}

Like all event handlers in .NET, this handler receives two parameters: the sender (in this case, the ListBox) and an object derived from EventArgs.

In the code shown, you cast the sender to the ListBox type (and consider it an exception if the sender is not a ListBox, as that is the only type of object that should be sending to this event handler).

You then check to make sure that the selectedItem is not null (during startup it is possible that it can be null). Assuming it is not null, you cast the selectedItem to an ImageURL, extract the Name property, and assign it to a temporary variable, chosenName, which we then in turn assign to the title of the window.

The interim variable is useful only for debugging; there is no other reason not to write:

Title = (lb.SelectedItem as ImageURL).Name.ToString(  );

Tip

You can also get at both the currently selected president’s ImageURL and the previously selected ImageURL through the SelectionChangedEventArgs parameter.

The Complete XAML File

If you’re not sitting in front of your computer right now, Example 19-8 has the complete XAML listing. Please replace the ellipses (…) in the URLs in this listing with:

"http://www.whitehouse.gov/history/presidents/images

We omitted this long URL from the listing to save space.

Example 19-8. Here is the complete XAML listing for the Presidential Browser application

<Window x:Class="Presidential_Browser.Window1"
   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   xmlns:local="clr-namespace:Presidential_Browser"
   Title="Window1" Height="300" Width="300">
   <Window.Resources>
      <LinearGradientBrush x:Key="ListBoxGradient"
         StartPoint="0,0" EndPoint="0,1">
         <GradientStop Color="#90000000" Offset="0" />
         <GradientStop Color="#40000000" Offset="0.005" />
         <GradientStop Color="#10000000" Offset="0.04" />
         <GradientStop Color="#20000000" Offset="0.945" />
         <GradientStop Color="#60FFFFFF" Offset="1" />
      </LinearGradientBrush>
      <Style x:Key="SpecialListStyle" TargetType="{x:Type ListBox}">
         <Setter Property="Template">
            <Setter.Value>
               <ControlTemplate TargetType="{x:Type ListBox}" >
                  <Border BorderBrush="Gray"
                          BorderThickness="1"
                          CornerRadius="6"
                          Background=
                            "{DynamicResource ListBoxGradient}" >
                     <ScrollViewer 
                         VerticalScrollBarVisibility="Disabled" 
                         HorizontalScrollBarVisibility="Visible">
                        <StackPanel IsItemsHost="True" 
                                    Orientation="Horizontal" 
                                    HorizontalAlignment="Left" />
                     </ScrollViewer>
                  </Border>
               </ControlTemplate>
            </Setter.Value>
         </Setter>
      </Style>
      <Style x:Key="SpecialListItem" 
             TargetType="{x:Type ListBoxItem}">
         <Setter Property="MaxHeight"  Value="75" />
         <Setter Property="MinHeight"  Value="75" />
         <Setter Property="Opacity"    Value=".75" />
         <Style.Triggers>
            <EventTrigger RoutedEvent="Mouse.MouseEnter">
               <EventTrigger.Actions>
                  <BeginStoryboard>
                     <Storyboard>
                        <DoubleAnimation Duration="0:0:0.2" 
                           Storyboard.TargetProperty="MaxHeight"  
                           To="85" />
                        <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="MaxHeight" />
                        <DoubleAnimation Duration="0:0:0.2" 
                           Storyboard.TargetProperty="Opacity" />
                     </Storyboard>
                  </BeginStoryboard>
               </EventTrigger.Actions>
            </EventTrigger>
         </Style.Triggers>
      </Style>
      <local:Images x:Key="Presidents">
         <local:ImageURL ImageURI=".../gw1.gif" Name="George Washington" />
         <local:ImageURL ImageURI=".../ja2.gif" Name="John Adams" />
         <local:ImageURL ImageURI=".../tj3.gif" Name="Thomas Jefferson" />
         <local:ImageURL ImageURI=".../jm4.gif" Name="James Madison" />
         <local:ImageURL ImageURI=".../jm5.gif" Name="James Monroe" />
         <local:ImageURL ImageURI=".../ja6.gif" Name="John Quincy Adams" />
         <local:ImageURL ImageURI=".../aj7.gif" Name="Andrew Jackson" />
         <local:ImageURL ImageURI=".../mb8.gif" Name="Martin Van Buren" />
         <local:ImageURL ImageURI=".../wh9.gif" Name="William H. Harrison" />
         <local:ImageURL ImageURI=".../jt10_1.gif" Name="John Tyler" />
         <local:ImageURL ImageURI=".../jp11.gif" Name="James K. Polk" />
         <local:ImageURL ImageURI=".../zt12.gif" Name="Zachary Taylor" />
         <local:ImageURL ImageURI=".../mf13.gif" Name="Millard Fillmore" />
         <local:ImageURL ImageURI=".../fp14.gif" Name="Franklin Pierce" />
         <local:ImageURL ImageURI=".../jb15.gif" Name="James Buchanan" />
         <local:ImageURL ImageURI=".../al16.gif" Name="Abraham Lincoln" />
         <local:ImageURL ImageURI=".../aj17.gif" Name="Andrew Johnson" />
         <local:ImageURL ImageURI=".../ug18.gif" Name="Ulysses S. Grant" />
         <local:ImageURL ImageURI=".../rh19.gif" Name="Rutherford B. Hayes" />
         <local:ImageURL ImageURI=".../jg20.gif" Name="James Garfield" />
      </local:Images>

   </Window.Resources>
   <Grid Width="300" Height="190" 
         DataContext="{StaticResource Presidents}">
      <Grid.RowDefinitions>
         <RowDefinition Height="20" />
         <RowDefinition Height="*" />
      </Grid.RowDefinitions>
      <StackPanel Grid.Row="0">
         <TextBlock FontSize="14" Grid.Row="0" >
             United States Presidents</TextBlock>
      </StackPanel>
      <StackPanel Grid.Row="1" Grid.ColumnSpan="3" >
           <ListBox Style="{StaticResource SpecialListStyle}" 
                   Name="PresPhotoListBox" Margin="0,0,0,20" 
                   SelectionChanged="PresPhotoListBox_SelectionChanged" 
                   ItemsSource="{Binding }" 
                   IsSynchronizedWithCurrentItem="True" 
                   SelectedIndex="0" 
                   ItemContainerStyle=
                         "{StaticResource SpecialListItem}" >
             <ListBox.ItemTemplate>
                <DataTemplate>
                   <Border VerticalAlignment="Center" 
                           HorizontalAlignment="Center" 
                           Padding="4" Margin="2" Background="White">
                      <Image Source="{Binding Path=ImageURI}" />
                   </Border>
                </DataTemplate>
            </ListBox.ItemTemplate>
         </ListBox>
      </StackPanel>
   </Grid>
</Window>

Run the application now, and you should see that it looks like Figure 19-7 (shown previously). The individual images animate when you mouse over them, and clicking one changes the title bar of the window to show the president’s name.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset