User Tools

Site Tools


notes:wpf:examples

Examples of using WPF

Example #1

This example displays a collection of books in a ListBox. The books are obtained from Simple Data Layer. It is also possible to update the books and add the new ones:

Points of interest:

  • DataContext
  • ListBox.IsSynchronizedWithCurrentItem
  • ListBox.ItemTemplate
  • ListBox.ItemContainerStyle

How DataContext works: When a WPF element uses binding but does not specify the Source, ElementName, or RelativeSource property, WPF checks the DataContext of that element. If it's null, WPF searches up the element tree looking for the first data context that isn't null. If it finds a data context, it uses it for the binding.

<Window x:Class="WBS.ExampleSimple.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="400" Width="550">
 
    <Window.Resources>
        <!-- Title -->
        <Style x:Key="TitleText" TargetType="TextBlock">
            <Setter Property="FontSize" Value="24"/>
            <Setter Property="Padding" Value="8,8,5,8"/>
            <Setter Property="Foreground" Value="SteelBlue" />
            <Setter Property="Background" Value="AliceBlue" />
        </Style>
 
        <!-- BookList item text -->
        <Style x:Key="BookListItemText" TargetType="TextBlock">
            <Setter Property="FontSize" Value="12"/>
            <Setter Property="Padding" Value="3"/>
            <Setter Property="TextWrapping" Value="Wrap" />
            <Setter Property="Width" Value="250" />
        </Style>
 
        <!-- BookList item header -->
        <Style x:Key="BookListItemHeader" TargetType="TextBlock" 
            BasedOn="{StaticResource BookListItemText}">
            <Setter Property="FontWeight" Value="Bold" />
            <Setter Property="Padding" Value="5,3"/>
        </Style>
 
        <!-- BookList item style -->
        <Style x:Key="BookListItemStyle" TargetType="{x:Type ListBoxItem}">
            <Style.Triggers>
                <Trigger Property="ItemsControl.AlternationIndex" Value="0">
                    <Setter Property="Background" Value="White" />
                </Trigger>
                <Trigger Property="ItemsControl.AlternationIndex" Value="1">
                    <Setter Property="Background" Value="WhiteSmoke" />
                </Trigger>
                <Trigger Property="IsSelected" Value="True">
                    <Setter Property="Foreground" Value="White" />
                </Trigger>
            </Style.Triggers>
        </Style>
 
        <!-- BookList style -->
        <Style x:Key="BookListStyle" TargetType="{x:Type ListBox}">
            <Setter Property="BorderThickness" Value="1" />
            <Setter Property="AlternationCount" Value="2" />
        </Style>
 
        <!-- BookList item data template -->
        <DataTemplate x:Key="BookListItemTemplate">
            <StackPanel Orientation="Horizontal">
                <TextBlock Text="{Binding Title}" Style="{StaticResource BookListItemText}" />
                <TextBlock Text="{Binding Authors}" Style="{StaticResource BookListItemText}" />
            </StackPanel>
        </DataTemplate>
 
        <!-- Label -->
        <Style x:Key="LabelText" TargetType="TextBlock">
            <Setter Property="FontSize" Value="13"/>
            <Setter Property="Padding" Value="3"/>
            <Setter Property="VerticalAlignment" Value="Center"/>
            <Setter Property="Margin" Value="5,0,0,0" />
        </Style>
 
        <!-- Book details -->
        <Style x:Key="BookDetails" TargetType="StackPanel">
            <Setter Property="Background" Value="AliceBlue"/>
        </Style>
 
        <!-- Separator -->
        <Style x:Key="Separator" TargetType="Border">
            <Setter Property="BorderThickness" Value="0 0 0 1"/>
            <Setter Property="BorderBrush" Value="SteelBlue"/>
        </Style>
    </Window.Resources>
 
    <DockPanel x:Name="BookCatalogContainer" LastChildFill="True">
 
        <!-- Title -->
        <StackPanel DockPanel.Dock="Top">
            <TextBlock Style="{StaticResource TitleText}">Books</TextBlock>
            <Border Style="{StaticResource Separator}" />
        </StackPanel>
 
        <!-- Status bar -->
        <StatusBar DockPanel.Dock="Bottom" xml:space="preserve">WBS - 2012</StatusBar>
 
        <!-- BookList header -->
        <StackPanel DockPanel.Dock="Top" Orientation="Horizontal">
            <TextBlock Style="{StaticResource BookListItemHeader}">Title</TextBlock>
            <TextBlock Style="{StaticResource BookListItemHeader}">Authors</TextBlock>
        </StackPanel>
 
        <!-- Book details form -->
        <StackPanel DockPanel.Dock="Bottom" Style="{StaticResource BookDetails}">
            <Border Style="{StaticResource Separator}" />
 
            <Grid Margin="0,5">
                <Grid.RowDefinitions>
                    <RowDefinition />
                    <RowDefinition />
                    <RowDefinition />
                </Grid.RowDefinitions>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="0.5*" />
                    <ColumnDefinition Width="2.5*" />
                    <ColumnDefinition Width="0.5*" />
                    <ColumnDefinition Width="0.8*" />
                </Grid.ColumnDefinitions>
 
                <TextBlock Grid.Column="0" Grid.Row="0"
                           Style="{StaticResource LabelText}">Title:</TextBlock>
                <TextBox   Grid.Column="1" Grid.Row="0" Text="{Binding Title}" />
                <TextBlock Grid.Column="0" Grid.Row="1"
                           Style="{StaticResource LabelText}">Authors:</TextBlock>
                <TextBox   Grid.Column="1" Grid.Row="1" Text="{Binding Authors}" />
                <TextBlock Grid.Column="2" Grid.Row="0"
                           Style="{StaticResource LabelText}">ISBN:</TextBlock>
                <TextBox   Grid.Column="3" Grid.Row="0" Text="{Binding Isbn13}" />
                <TextBlock Grid.Column="2" Grid.Row="1"
                           Style="{StaticResource LabelText}">Price:</TextBlock>
                <TextBox   Grid.Column="3" Grid.Row="1" Text="{Binding Price}" />
 
                <Button x:Name="AddBook" Grid.Column="3" Grid.Row="2" 
                        Content="Add Book" 
                        Margin="0,5,0,0"
                        Click="AddBook_Click" />
            </Grid>
        </StackPanel>
 
        <!-- Books -->
 
        <!-- Because the IsSynchronizedWithCurrentItem property is set to true, the ListBox
             keeps the SelectedItem synchronized with the current item in the Items property. 
             As a result, other controls that bind to the same collection (like the TextBoxes)
             bind to the values of the currently selected item in the ListBox -->
        <ListBox x:Name="BookList"
                 IsSynchronizedWithCurrentItem="True"
                 ItemsSource="{Binding}"
                 Style="{StaticResource BookListStyle}"
                 ItemTemplate="{StaticResource BookListItemTemplate}"
                 ItemContainerStyle="{StaticResource BookListItemStyle}">
        </ListBox>
 
    </DockPanel>
</Window>
using System;
using System.Collections.ObjectModel; // ObservableCollection<T>
using System.Windows;
using System.Windows.Controls;
 
using WBS.DataLayerSimple;
 
namespace WBS.ExampleSimple
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            Loaded += new RoutedEventHandler(MainWindow_Loaded);
        }
 
        void MainWindow_Loaded(object sender, RoutedEventArgs e)
        {
            // Make data available for binding for all the children: the ListBox and the TextBoxes.
 
            // Because neither the ListBox nor the TextBoxes has DataContext defined, they look up 
            // the control hierarchy to determine their DataContext. As they have a common parent 
            // BookCatalogContainer, the parent's DataContext is used for binding of the ListBox 
            // and the TextBoxes.
            BookCatalogContainer.DataContext = BookCatalog.GetSampleBooks();
        }
 
        private void AddBook_Click(object sender, RoutedEventArgs e)
        {
            BookCatalog.AddBook(
                new Book { 
                    Title = "New Book", 
                    Authors = "", 
                    Isbn13 = "999-9999999999", 
                    PubDate = DateTime.Today, 
                    Price = 0.0M, 
                    Rating = 0.0F, 
                    IsKindle = false });
        }
    }
}

Download a solution containing both Simple Example and Simple Data Layer: Example #1

Example #2

This example builds upon the Example #1. It uses a Nested Data Source that provides a Parent-Child collection represented by the Publisher-Books relationship. It also adds a toolbar with a ComboBox that allows users to select a publisher and view the books assigned to it.

Points of interest:

  • DockPanel.DataContext and ComboBox.ItemsSource are set to the parent collection (Publishers)
  • ComboBox.IsSynchronizedWithCurrentItem synchronizes a publisher selected in the ComboBox with the books displayed in the ListBox
  • ListBox.ItemsSource is set to the nested collection (Books)
  • ListBox.IsSynchronizedWithCurrentItem synchronizes a book selected in the ListBox with TextBoxes displaying details of the selected book
  • StackPanel.DataContext binds to ListBox.SelectedItem

Note that overall this example is very similar to Example #1. It differs only in a few places:

  • It adds a ToolBar represented by the ToolBarTray control
  • It adds a style defined for a ComboBox located in the ToolBar
  • Bindings for the ListBox 'BookList' is changed to ItemsSource=“{Binding Books}” in order to bind to the nested collection
  • The DataContext of the StackPanel containing book details is set to “{Binding ElementName=BookList, Path=SelectedItem}”
  • The BookCatalog.AddBook method accepts one additional parameter: the code of a publisher selected in the ComboBox.
<Window x:Class="WBS.ExampleNested.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="400" Width="550">
 
    <Window.Resources>
        <!-- Title -->
        <Style x:Key="TitleText" TargetType="TextBlock">
            <Setter Property="FontSize" Value="24"/>
            <Setter Property="Padding" Value="8,8,5,8"/>
            <Setter Property="Foreground" Value="SteelBlue" />
            <Setter Property="Background" Value="AliceBlue" />
        </Style>
 
        <!-- BookList item text -->
        <Style x:Key="BookListItemText" TargetType="TextBlock">
            <Setter Property="FontSize" Value="12"/>
            <Setter Property="Padding" Value="3"/>
            <Setter Property="TextWrapping" Value="Wrap" />
            <Setter Property="Width" Value="250" />
        </Style>
 
        <!-- BookList item header -->
        <Style x:Key="BookListItemHeader" TargetType="TextBlock" BasedOn="{StaticResource BookListItemText}">
            <Setter Property="FontWeight" Value="Bold" />
            <Setter Property="Padding" Value="5,3"/>
        </Style>
 
        <!-- BookList item style -->
        <Style x:Key="BookListItemStyle" TargetType="{x:Type ListBoxItem}">
            <Style.Triggers>
                <Trigger Property="ItemsControl.AlternationIndex" Value="0">
                    <Setter Property="Background" Value="White" />
                </Trigger>
                <Trigger Property="ItemsControl.AlternationIndex" Value="1">
                    <Setter Property="Background" Value="WhiteSmoke" />
                </Trigger>
                <Trigger Property="IsSelected" Value="True">
                    <Setter Property="Foreground" Value="White" />
                </Trigger>
            </Style.Triggers>
        </Style>
 
        <!-- BookList style -->
        <Style x:Key="BookListStyle" TargetType="{x:Type ListBox}">
            <Setter Property="BorderThickness" Value="1" />
            <Setter Property="AlternationCount" Value="2" />
        </Style>
 
        <!-- BookList item data template -->
        <DataTemplate x:Key="BookListItemTemplate">
            <StackPanel Orientation="Horizontal">
                <TextBlock Text="{Binding Title}" Style="{StaticResource BookListItemText}" />
                <TextBlock Text="{Binding Authors}" Style="{StaticResource BookListItemText}" />
            </StackPanel>
        </DataTemplate>
 
        <!-- Label -->
        <Style x:Key="LabelText" TargetType="TextBlock">
            <Setter Property="FontSize" Value="13"/>
            <Setter Property="Padding" Value="3"/>
            <Setter Property="VerticalAlignment" Value="Center"/>
            <Setter Property="Margin" Value="5,0,0,0" />
        </Style>
 
        <!-- Book details -->
        <Style x:Key="BookDetails" TargetType="StackPanel">
            <Setter Property="Background" Value="AliceBlue"/>
        </Style>
 
        <!-- Separator -->
        <Style x:Key="Separator" TargetType="Border">
            <Setter Property="BorderThickness" Value="0 0 0 1"/>
            <Setter Property="BorderBrush" Value="SteelBlue"/>
        </Style>
 
        <!-- Toolbar ComboBox -->
        <Style x:Key="ToolbarComboBox" TargetType="ComboBox">
            <Setter Property="Width" Value="Auto"/>
            <Setter Property="FontSize" Value="13"/>
            <Setter Property="Height" Value="22"/>
            <Setter Property="IsEditable" Value="False"/>
        </Style>
    </Window.Resources>
 
    <DockPanel x:Name="BookCatalogContainer" LastChildFill="True">
 
        <!-- Toolbar -->
        <ToolBarTray DockPanel.Dock="Top">
            <ToolBar>
                <StackPanel Orientation="Horizontal">
                    <TextBlock Text="Publisher: " 
                               Style="{StaticResource LabelText}" />
                    <!-- IsSynchronizedWithCurrentItem sets the item to the first publisher -->
                    <ComboBox x:Name="cboPublisher"
                              DisplayMemberPath="PubName"
                              SelectedValuePath="PubCode"
                              ItemsSource="{Binding}"
                              Style="{StaticResource ToolbarComboBox}" 
                              IsSynchronizedWithCurrentItem="True">
                    </ComboBox>
                    <!-- The number of books for the selected publisher -->
                    <!-- Note that we're binding to the Publisher.BookCount property -->
                    <TextBlock Text="Book count: " Style="{StaticResource LabelText}" Margin="15,0,0,0" />
                    <TextBlock Text="{Binding BookCount}" Style="{StaticResource LabelText}" />
                </StackPanel>
            </ToolBar>
        </ToolBarTray>
 
        <!-- Title -->
        <StackPanel DockPanel.Dock="Top">
            <TextBlock Style="{StaticResource TitleText}">Books</TextBlock>
            <Border Style="{StaticResource Separator}" />
        </StackPanel>
 
        <!-- Status bar -->
        <StatusBar DockPanel.Dock="Bottom" xml:space="preserve">WBS - 2012</StatusBar>
 
        <!-- BookList header -->
        <StackPanel DockPanel.Dock="Top" Orientation="Horizontal">
            <TextBlock Style="{StaticResource BookListItemHeader}">Title</TextBlock>
            <TextBlock Style="{StaticResource BookListItemHeader}">Authors</TextBlock>
        </StackPanel>
 
        <!-- Book details form -->
        <!-- The StackPanel is bound to the SelectedItem property of the BookList ListBox -->
        <StackPanel DockPanel.Dock="Bottom" 
                    Style="{StaticResource BookDetails}" 
                    DataContext="{Binding ElementName=BookList, Path=SelectedItem}">
            <Border Style="{StaticResource Separator}" />
 
            <Grid Margin="0,5">
                <Grid.RowDefinitions>
                    <RowDefinition />
                    <RowDefinition />
                    <RowDefinition />
                </Grid.RowDefinitions>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="0.5*" />
                    <ColumnDefinition Width="2.5*" />
                    <ColumnDefinition Width="0.5*" />
                    <ColumnDefinition Width="0.8*" />
                </Grid.ColumnDefinitions>
 
                <TextBlock Grid.Column="0" Grid.Row="0"
                           Style="{StaticResource LabelText}">Title:</TextBlock>
                <TextBox   Grid.Column="1" Grid.Row="0" Text="{Binding Title}" />
                <TextBlock Grid.Column="0" Grid.Row="1"
                           Style="{StaticResource LabelText}">Authors:</TextBlock>
                <TextBox   Grid.Column="1" Grid.Row="1" Text="{Binding Authors}" />
                <TextBlock Grid.Column="2" Grid.Row="0"
                           Style="{StaticResource LabelText}">ISBN:</TextBlock>
                <TextBox   Grid.Column="3" Grid.Row="0" Text="{Binding Isbn13}" />
                <TextBlock Grid.Column="2" Grid.Row="1"
                           Style="{StaticResource LabelText}">Price:</TextBlock>
                <TextBox   Grid.Column="3" Grid.Row="1" Text="{Binding Price}" />
 
                <Button x:Name="AddBook" Grid.Column="3" Grid.Row="2" 
                        Content="Add Book" 
                        Margin="0,5,0,0"
                        Click="AddBook_Click" />
            </Grid>
        </StackPanel>
 
        <!-- Books for the selected publisher -->
        <ListBox x:Name="BookList"
                 IsSynchronizedWithCurrentItem="True"
                 ItemsSource="{Binding Books}"
                 Style="{StaticResource BookListStyle}"
                 ItemTemplate="{StaticResource BookListItemTemplate}"
                 ItemContainerStyle="{StaticResource BookListItemStyle}">
        </ListBox>
 
    </DockPanel>
</Window>
using System;
using System.ComponentModel; // SortDescription
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data; // ListCollectionView
 
using WBS.DataLayerNested;
 
namespace WBS.ExampleNested
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            Loaded += new RoutedEventHandler(MainWindow_Loaded);
        }
 
        void MainWindow_Loaded(object sender, RoutedEventArgs e)
        {
            BookCatalogContainer.DataContext = BookCatalog.GetSampleBooks();
        }
 
        private void AddBook_Click(object sender, RoutedEventArgs e)
        {
            string pubCode = (string)cboPublisher.SelectedValue;
 
            BookCatalog.AddBook(
                pubCode,
                new Book
                {
                    Title = "New Book",
                    Authors = "",
                    Isbn13 = "999-9999999999",
                    PubDate = DateTime.Today,
                    Price = 0.0M,
                    Rating = 0.0F,
                    IsKindle = false
                });
        }
    }
}

Download a solution containing both Nested Example and Nested Data Layer: Example #2

Example #3

This example displays a collection of books in a DataGrid. It also shows how to use a data view to implement custom navigation, filtering, and sorting. Although sorting could be provided using the DataGrid, for the sake of example it is implemented using the data view. The collection of books is obtained from Simple Data Layer.

A data view is an intermediate layer between the data source and a bound control such as a ListBox or a ComboBox (which are examples of an ItemsControl).

Features of data views:

  • Determine the number of items in the list using the Count property
  • Get a reference to the current data object using CurrentItem
  • Get the current position index using CurrentPosition
  • Navigate between records using MovieCurrentToFirst, MoveCurrentToNext etc.
  • Sort data using the SortDescriptions collection
  • Apply custom sorting using the CustomSort property of the ListCollectionView that accepts an IComparer object
  • Filter data using the Filter property
  • Group data (i.e. create subcollections that can be navigated separately) using the CollectionView.GroupDescriptions collection and System.ComponentModel.PropertyGroupDescription objects

A good practice: For performance reasons, use an ObservableCollection (or any other collection that implements IList) as the data source rather than an IEnumerable collection. In such case a specialized ListCollectionView is used which derives from more generic CollectionView.

<Window x:Class="WBS.ExampleDataView.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="400" Width="530">
 
    <Window.Resources>
        <!-- Title -->
        <Style x:Key="TitleText" TargetType="TextBlock">
            <Setter Property="FontSize" Value="24"/>
            <Setter Property="Padding" Value="8,8,5,8"/>
            <Setter Property="Foreground" Value="SteelBlue" />
            <Setter Property="Background" Value="AliceBlue" />
        </Style>
 
        <!-- Label -->
        <Style x:Key="LabelText" TargetType="TextBlock">
            <Setter Property="FontSize" Value="13"/>
            <Setter Property="Padding" Value="3"/>
            <Setter Property="VerticalAlignment" Value="Center"/>
            <Setter Property="Margin" Value="5,0,0,0" />
        </Style>
 
        <!-- Toolbar text -->
        <Style x:Key="ToolbarText" TargetType="TextBlock">
            <Setter Property="VerticalAlignment" Value="Center"/>
            <Setter Property="Margin" Value="3" />
        </Style>
 
        <!-- Navigation button -->
        <Style x:Key="NavigationButton" TargetType="Button">
            <Setter Property="Width" Value="20"/>
            <Setter Property="Height" Value="20"/>
            <Setter Property="Margin" Value="3,3" />
        </Style>
 
        <!-- Book details -->
        <Style x:Key="BookDetails" TargetType="StackPanel">
            <Setter Property="Background" Value="AliceBlue"/>
        </Style>
 
        <!-- BookList cell -->
        <Style x:Key="BookListCellStyle" TargetType="DataGridCell">
            <Setter Property="FontSize" Value="12"/>
            <Setter Property="Padding" Value="2"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type DataGridCell}">
                        <Border Padding="{TemplateBinding Padding}" 
                                BorderBrush="{TemplateBinding BorderBrush}" 
                                BorderThickness="{TemplateBinding BorderThickness}" 
                                Background="{TemplateBinding Background}" 
                                SnapsToDevicePixels="True">
                            <ContentPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
                        </Border>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
 
        </Style>
    </Window.Resources>
 
    <DockPanel x:Name="BookCatalogContainer" LastChildFill="True">
 
        <!-- Tool bar -->
        <ToolBarTray DockPanel.Dock="Top">
            <ToolBar>
                <TextBlock Style="{StaticResource ToolbarText}">Find in title:</TextBlock>
                <TextBox x:Name="txtTitleFilter" Width="100" />
                <Button x:Name="btnApplyFilter" Content="Apply Filter" Click="btnApplyFilter_Click" />
                <Button x:Name="btnRemoveFilter" Content="Remove Filter" Click="btnRemoveFilter_Click" />
                <Separator />
                <TextBlock Style="{StaticResource ToolbarText}">Sort by:</TextBlock>
                <Button x:Name="btnSortByTitle" Content="Title" Click="btnSortByTitle_Click" />
                <Button x:Name="btnSortByPrice" Content="Price" Click="btnSortByPrice_Click" />
                <Button x:Name="btnSortByRating" Content="Rating" Click="btnSortByRating_Click" />
            </ToolBar>
        </ToolBarTray>
 
        <Grid DockPanel.Dock="Top">
            <!-- Title -->
            <TextBlock Style="{StaticResource TitleText}">Books</TextBlock>
            <!-- Navigation buttons -->
            <StackPanel Orientation="Horizontal" 
                        HorizontalAlignment="Right"
                        Margin="5,0">
                <Button x:Name="btnFirst" Click="btnFirst_Click"
                        Style="{StaticResource NavigationButton}" Content="&lt;&lt;" />
                <Button x:Name="btnPrevious" Click="btnPrevious_Click"
                        Style="{StaticResource NavigationButton}" Content="&lt;" />
                <TextBlock x:Name="lblNavigation" Style="{StaticResource LabelText}" />
                <Button x:Name="btnNext" Click="btnNext_Click"
                        Style="{StaticResource NavigationButton}" Content="&gt;" />
                <Button x:Name="btnLast" Click="btnLast_Click"
                        Style="{StaticResource NavigationButton}" Content="&gt;&gt;" />
 
            </StackPanel>
        </Grid>
 
        <!-- Status bar -->
        <StatusBar DockPanel.Dock="Bottom" xml:space="preserve">WBS - 2013</StatusBar>
 
        <!-- Book details -->
        <StackPanel DockPanel.Dock="Bottom" Style="{StaticResource BookDetails}">
            <Grid Margin="0,5">
                <Grid.RowDefinitions>
                    <RowDefinition />
                    <RowDefinition />
                </Grid.RowDefinitions>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="0.4*" />
                    <ColumnDefinition Width="*" />
                    <ColumnDefinition Width="0.4*" />
                    <ColumnDefinition Width="*" />
                </Grid.ColumnDefinitions>
 
                <TextBlock Grid.Column="2" Grid.Row="0"
                           Style="{StaticResource LabelText}">PubDate:</TextBlock>
                <TextBlock Grid.Column="3" Grid.Row="0"
                           Style="{StaticResource LabelText}" Text="{Binding PubDate, StringFormat=d}" />
 
                <TextBlock Grid.Column="2" Grid.Row="1"
                           Style="{StaticResource LabelText}">Rating:</TextBlock>
                <TextBlock Grid.Column="3" Grid.Row="1" 
                           Style="{StaticResource LabelText}" Text="{Binding Rating, StringFormat={}{0:F1}}" />
 
                <TextBlock Grid.Column="0" Grid.Row="0"
                           Style="{StaticResource LabelText}">ISBN:</TextBlock>
                <TextBlock Grid.Column="1" Grid.Row="0" 
                           Style="{StaticResource LabelText}" Text="{Binding Isbn13}" />
 
                <TextBlock Grid.Column="0" Grid.Row="1"
                           Style="{StaticResource LabelText}">Price:</TextBlock>
                <TextBlock Grid.Column="1" Grid.Row="1" 
                           Style="{StaticResource LabelText}" Text="{Binding Price, StringFormat={}{0:C}}" />
            </Grid>
        </StackPanel>
 
        <!-- Book list -->
        <DataGrid x:Name="BookList"
                  IsSynchronizedWithCurrentItem="True"
                  IsReadOnly="True"
                  AutoGenerateColumns="False" 
                  HeadersVisibility="All" 
                  AlternatingRowBackground="WhiteSmoke" 
                  VerticalGridLinesBrush="Gray" HorizontalGridLinesBrush="Gray"
                  CanUserSortColumns="False" CanUserReorderColumns="False" 
                  CanUserAddRows="False" CanUserDeleteRows="False"
                  ItemsSource="{Binding}"
                  CellStyle="{StaticResource BookListCellStyle}">
            <DataGrid.Columns>
                <DataGridTextColumn Header="Title" Width="250" FontSize="12" Binding="{Binding Title}" />
                <DataGridTextColumn Header="Authors" Width="250" FontSize="12" Binding="{Binding Authors}" />
            </DataGrid.Columns>
        </DataGrid>
 
    </DockPanel>
</Window>
using System;
using System.Collections.ObjectModel; // ObservableCollection<T>
using System.ComponentModel; // SortDescription
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data; // ListCollectionView
 
using WBS.DataLayerSimple;
 
namespace WBS.ExampleDataView
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        // A reference to the DataView.
        private ListCollectionView bookView;
 
        // A reference to the DataView filter.
        private BookTitleFilter bookTitleFilter;
 
        public MainWindow()
        {
            InitializeComponent();
            Loaded += new RoutedEventHandler(MainWindow_Loaded);
        }
 
        void MainWindow_Loaded(object sender, RoutedEventArgs e)
        {
            // Populate the container (a StackPanel) with data.
            BookCatalogContainer.DataContext = BookCatalog.GetSampleBooks();
 
            // Get a reference to the DataView and handle the CurrentChanged event.
            // GetDefaultView() returns ICollectionView. That's why we cast it to ListCollectionView.
            bookView = (ListCollectionView)CollectionViewSource.GetDefaultView(BookCatalogContainer.DataContext);
            bookView.CurrentChanged += new EventHandler(bookView_CurrentChanged);
 
            // Apply default sorting.
            bookView.SortDescriptions.Add(new SortDescription("Title", ListSortDirection.Ascending));
 
            // Move to the first element in the DataView.
            bookView.MoveCurrentToFirst();
 
            // Create filters.
            bookTitleFilter = new BookTitleFilter();
        }
 
        private void bookView_CurrentChanged(object sender, EventArgs e)
        {
            lblNavigation.Text = String.Format("Record {0} of {1}",
                bookView.CurrentPosition + 1,
                bookView.Count);
 
            // Enable or disable certain buttons depending on the current position.
            btnFirst.IsEnabled = (bookView.CurrentPosition > 0);
            btnPrevious.IsEnabled = (bookView.CurrentPosition > 0);
            btnNext.IsEnabled = (bookView.CurrentPosition < bookView.Count - 1);
            btnLast.IsEnabled = (bookView.CurrentPosition < bookView.Count - 1);
        }
 
        private void btnFirst_Click(object sender, RoutedEventArgs e)
        {
            bookView.MoveCurrentToFirst();
        }
 
        private void btnPrevious_Click(object sender, RoutedEventArgs e)
        {
            // The code to prevent from moving before the first item 
            // is not necessary here as we disable the buttons dynamically.
            if (bookView.CurrentPosition > 0)
                bookView.MoveCurrentToPrevious();
        }
 
        private void btnNext_Click(object sender, RoutedEventArgs e)
        {
            // The code to prevent from moving after the first item 
            // is not necessary here as we disable the buttons dynamically.
            if (bookView.CurrentPosition < bookView.Count - 1)
                bookView.MoveCurrentToNext();
        }
 
        private void btnLast_Click(object sender, RoutedEventArgs e)
        {
            bookView.MoveCurrentToLast();
        }
 
        private void btnApplyFilter_Click(object sender, RoutedEventArgs e)
        {
            string filter = txtTitleFilter.Text;
 
            if (String.IsNullOrEmpty(filter))
                FilterBooksByTitle(null);
            else
                FilterBooksByTitle(filter);
        }
 
        private void btnRemoveFilter_Click(object sender, RoutedEventArgs e)
        {
            txtTitleFilter.Text = "";
            FilterBooksByTitle(null);
        }
 
        private void btnSortByTitle_Click(object sender, RoutedEventArgs e)
        {
            bookView.SortDescriptions.Clear();
            bookView.SortDescriptions.Add(new SortDescription("Title", ListSortDirection.Ascending));
        }
 
        private void btnSortByPrice_Click(object sender, RoutedEventArgs e)
        {
            bookView.SortDescriptions.Clear();
            bookView.SortDescriptions.Add(new SortDescription("Price", ListSortDirection.Ascending));
        }
 
        private void btnSortByRating_Click(object sender, RoutedEventArgs e)
        {
            bookView.SortDescriptions.Clear();
            bookView.SortDescriptions.Add(new SortDescription("Rating", ListSortDirection.Descending));
            bookView.SortDescriptions.Add(new SortDescription("Title", ListSortDirection.Ascending));
        }
 
        #region Filtering
        private void FilterBooksByTitle(string titleFragment)
        {
            if (titleFragment == null)
            {
                // Remove the filter. No need to refresh the view.
                bookView.Filter = null;
            }
            else
            {
                // Set the filtering criteria.
                bookTitleFilter.TitleFragment = titleFragment;
 
                // The Filter property accepts a Predicate delegate that
                // points to a custom filtering method.
                bookView.Filter = new Predicate<object>(bookTitleFilter.ApplyFilter);
 
                // Refilter the view.
                bookView.Refresh();
            }
        }
 
        // BookTitleFilter is a dedicated filtering class.
        // It wraps the filtering criteria and the method
        // that performs the filtering.
        // It's helpful to create a single filtering class 
        // for each window.
        private class BookTitleFilter
        {
            // Filtering criteria.
            public string TitleFragment { get; set; }
 
            // The custom filtering method examines a single data item and returns
            // true if it should be included and false otherwise.
            public bool ApplyFilter(object item)
            {
                Book book = item as Book;
                return (book.Title.ToLower().Contains(TitleFragment.ToLower()));
            }
        }
        #endregion
    }
}

Download a solution: Example #3

Stock Data Viewer

<Window x:Class="TestApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Vista Graph - Stock Data Viewer"
    Width="500" Height="500" Loaded="On_Loaded">
    <!-- Root element - Grid -->
    <Grid>
        <!-- Columns and rows definitions -->
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto" />
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="Auto" />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="*" />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>
 
        <!-- Label at the top -->
        <TextBlock Text="{Binding CompanyName}" Grid.ColumnSpan="3" 
                   HorizontalAlignment="Center" x:Name="stockName"
                   FontWeight="Bold" FontSize="30" Margin="20">
        </TextBlock>
 
        <!-- Margin: left,top,right,bottom -->
 
        <!-- Vertical axis labels-->
        <DockPanel Grid.Column="0" Grid.Row="1" Margin="10,0,3,0" Background="AliceBlue">
            <TextBlock Text="100" DockPanel.Dock="Top" x:Name="topPrice" />
            <TextBlock Text="10" DockPanel.Dock="Bottom" x:Name="bottomPrice" />
            <TextBlock Text="Price" VerticalAlignment="Center">
                <TextBlock.LayoutTransform>
                    <RotateTransform CenterX="0" CenterY="0" Angle="270" />
                </TextBlock.LayoutTransform>
            </TextBlock>
        </DockPanel>
 
        <!-- Horizontal axis labels -->
        <DockPanel Grid.Column="1" Grid.Row="2" Margin="0,3,0,10" Background="AliceBlue">
            <TextBlock Text="1/2/05" DockPanel.Dock="Left" x:Name="firstDate" />
            <TextBlock Text="2/3/05" DockPanel.Dock="Right" x:Name="lastDate" />
            <TextBlock Text="Date" HorizontalAlignment="Center" />
        </DockPanel>
 
        <!-- Nested grid -->
        <Grid Grid.Column="2" Grid.Row="1" Grid.RowSpan="2" Margin="20">
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto" />
                <RowDefinition Height="*" />
                <RowDefinition Height="Auto" />
            </Grid.RowDefinitions>
            <TextBlock Grid.Row="0">Companies:</TextBlock>
 
            <!-- ListBox -->
            <ListBox Grid.Row="1" x:Name="companyList" 
                     ItemsSource="{Binding}" 
                     IsSynchronizedWithCurrentItem="True"
                     VerticalAlignment="Top">
                <ListBox.ItemTemplate>
                    <DataTemplate>
                        <TextBlock Text="{Binding CompanyName}" />
                    </DataTemplate>
                </ListBox.ItemTemplate>
            </ListBox>
 
            <!-- Magnifying glass toggle button -->
            <ToggleButton Grid.Row="2" x:Name="magnifyButton">Magnify</ToggleButton>
 
        </Grid>
 
        <!-- Content/Graph -->
        <!-- 
        Viewbox:
        Defines a content decorator that can stretch and scale a single child to fill the available space.
        -->
        <Viewbox Grid.Column="1" Grid.Row="1" Stretch="Fill">
            <Grid>
 
                <Canvas x:Name="graphCanvas" Width="100" Height="100">
 
                    <!-- Background -->
                    <Canvas.Background>
                        <RadialGradientBrush RadiusX="1.5" RadiusY="1.5" Center="0.1,1.3">
                            <RadialGradientBrush.GradientStops>
                                <!-- Offset says how far through the fill the color should appear. 
                                It can have a value from 0 to 1. -->
                                <GradientStop Offset="0" Color="#00FFE000" />
                                <GradientStop Offset="1" Color="Beige" />
                            </RadialGradientBrush.GradientStops>
                        </RadialGradientBrush>
                    </Canvas.Background>
 
                    <!-- Graph field -->
                    <Rectangle x:Name="gridRectangle" Stroke="Gray" 
                               StrokeThickness="0.2" Width="100" Height="100" />
 
                    <!-- Axes -->
                    <Line Stroke="Black" StrokeThickness="0.5" X1="0" X2="0" Y1="0" Y2="100" />
                    <Line Stroke="Black" StrokeThickness="0.5" X1="0" X2="100" Y1="100" Y2="100" />
 
                    <!-- Example data line -->
                    <!-- Note: Polyline.Points collection has to start and end with the same value -->
                    <Polyline x:Name="dataLine" Stroke="Blue" StrokeThickness="1" />
                </Canvas>
 
                <!-- Magnify glass (initially hidden) -->
                <Canvas x:Name="magnifyCanvas" Width="100" Height="100">
                    <Ellipse x:Name="magnifyEllipse" Width="55" Height="55" Visibility="Hidden" />
                </Canvas>
            </Grid>
        </Viewbox>
    </Grid>
</Window>
using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Media;
 
namespace TestApp
{
    public partial class MainWindow : Window
    {
        private List<StockClosingPrices> list = null;
        private VisualBrush magnifyVisualBrush;
 
        public MainWindow()
        {
            InitializeComponent();
 
            // Load data to DataContext.
            list = StockData.LoadData();
            this.DataContext = list;
        }
 
        private void On_Loaded(object sender, RoutedEventArgs e)
        {
            // Bind event handlers
            companyList.SelectionChanged += new SelectionChangedEventHandler(companyList_SelectionChanged);
            magnifyButton.Checked += new RoutedEventHandler(magnifyButton_Checked);
            magnifyButton.Unchecked += new RoutedEventHandler(magnifyButton_Unchecked);
            magnifyCanvas.MouseMove += new System.Windows.Input.MouseEventHandler(magnifyCanvas_MouseMove);
            graphCanvas.MouseMove += new System.Windows.Input.MouseEventHandler(magnifyCanvas_MouseMove);
 
            DrawGrid(100, 100, 5, 5);
 
            // Grab the very first company
            StockClosingPrices company = list[0];
            DefinePoints(company);
        }
 
        void companyList_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            // Get selected company
            StockClosingPrices company = (StockClosingPrices)companyList.SelectedItem;
            DefinePoints(company);
        }
 
        private void DefinePoints(StockClosingPrices company)
        {
            // Find bottom and top price
            decimal min = company.Prices[0];
            decimal max = min;
            foreach (decimal price in company.Prices)
            {
                min = Math.Min(min, price);
                max = Math.Max(max, price);
            }
            bottomPrice.Text = min.ToString();
            topPrice.Text = max.ToString();
 
            // Show range for dates on the vertical axis
            firstDate.Text = "01/01/05";
            lastDate.Text = "12/31/05";
 
            // Calculate scale
            decimal range = max - min;
            double scale = (range == 0) ? 1.0 : 100.0 / ((double)range);
 
            // Create points
            Point[] points = new Point[company.Prices.Length];
            for (int i = 0; i < points.Length; ++i)
            {
                decimal price = company.Prices[i];
                decimal diffFromMax = max - price;
                double y = ((double)diffFromMax) * scale;
                double x = (i * 100) / ((double)(points.Length - 1));
                points[i] = new Point(x, y);
            }
 
            // Change the Points property on the Polyline.
            dataLine.Points = new PointCollection(points);
        }
 
        private void DrawGrid(double width, double height, int numRows, int numColumns)
        {
            // GeometryDrawing enables drawing using various shapes.
            // Other type of drawing classes:
            // - ImageDrawing (drawing using bitmaps)
            // - GlyphRunDrawing (drawing using texts)
            // - VideoDrawing (drawing using videos)
            // DrawingGroup - combines multiple drawings.
            GeometryDrawing grid = new GeometryDrawing();
 
            // Pen defines color and thickness of the grid lines.
            grid.Pen = new Pen(Brushes.DarkBlue, 0.2);
 
            // Shapes of type Geometry drawn by GeometryDrawing:
            // - EllipseGeometry
            // - LineGeometry
            // - RectangleGeometry
            // - PathGeometry
            // GeometryGroup - combines multiple geometries into one.
            GeometryGroup lines = new GeometryGroup();
 
            // Add the lines to the group.
            for (int row = 1; row < numRows; ++row)
            {
                double pos = (height * row) / ((double)numRows);
                LineGeometry line = new LineGeometry(new Point(0, pos), new Point(width, pos));
                lines.Children.Add(line);
            }
            for (int col = 1; col < numColumns; ++col)
            {
                double pos = (width * col) / ((double)numColumns);
                LineGeometry line = new LineGeometry(new Point(pos, 0), new Point(pos, height));
                lines.Children.Add(line);
            }
 
            // Assign the group of lines.
            grid.Geometry = lines;
 
            // Paint using the group of lines.
            DrawingBrush brush = new DrawingBrush(grid);
            gridRectangle.Fill = brush;
        }
 
        void magnifyButton_Checked(object sender, RoutedEventArgs e)
        {
            magnifyEllipse.Visibility = Visibility.Visible;
            if (magnifyVisualBrush == null)
            {
                magnifyVisualBrush = new VisualBrush(graphCanvas);
                magnifyVisualBrush.ViewboxUnits = BrushMappingMode.Absolute;
                magnifyEllipse.Fill = magnifyVisualBrush;
 
                // Set starting position
                double w = gridRectangle.Width;
                double h = gridRectangle.Height;
                Canvas.SetLeft(magnifyEllipse, w - magnifyEllipse.Width + magnifyEllipse.Width / 2);
                Canvas.SetTop(magnifyEllipse, h - magnifyEllipse.Height);
            }
        }
 
        void magnifyButton_Unchecked(object sender, RoutedEventArgs e)
        {
            magnifyEllipse.Visibility = Visibility.Hidden;
        }
 
        void magnifyCanvas_MouseMove(object sender, System.Windows.Input.MouseEventArgs e)
        {
            if (magnifyButton.IsChecked.GetValueOrDefault() && magnifyVisualBrush != null)
            {
                Point p = e.GetPosition(magnifyCanvas);
                if (p.X > 0 && p.X < 100 && p.Y > 0 && p.Y < 100)
                {
                    magnifyVisualBrush.Viewbox = new Rect(p.X - 4, p.Y - 4, 8, 8);
                    Canvas.SetLeft(magnifyEllipse, p.X - magnifyEllipse.Width / 2);
                    Canvas.SetTop(magnifyEllipse, p.Y - magnifyEllipse.Height / 2);
                }
            }
        }
    }
 
    public class StockClosingPrices
    {
        private string companyName;
        private decimal[] prices;
 
        public StockClosingPrices(string companyName, decimal[] prices)
        {
            this.companyName = companyName;
            this.prices = prices;
        }
 
        public string CompanyName { get { return companyName; } }
        public decimal[] Prices { get { return prices; } }
    }
 
    public class StockData
    {
        public static List<StockClosingPrices> LoadData()
        {
            List<StockClosingPrices> list = new List<StockClosingPrices>();
            list.Add(new StockClosingPrices("Wagabunda", 
                    new decimal[] { 10, 10.5M, 11, 13, 13.8M, 14.2M, 13, 11, 8, 7, 5, 5.1M, 5.7M, 
                        5.8M, 5.2M, 5.1M, 4.7M, 4.1M, 5.8M, 6.3M, 7.4M, 8.4M, 8.6M, 8.7M, 9.0M, 
                        9.3M, 9.1M, 9.7M, 9.9M, 10.2M }));
            list.Add(new StockClosingPrices("Mayapiki Frukty", 
                    new decimal[10] { 30, 34, 67, 45, 44, 23, 23, 12, 45, 78 }));
            list.Add(new StockClosingPrices("Zapadki Inc.", 
                    new decimal[10] { 2, 6, 12, 7, 8, 14, 17, 18, 10, 11 }));
            return list;
        }
    }
}

Vista Catalog

App.xaml

<Application x:Class="VistaCatalog.App"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    StartupUri="Window1.xaml">
    <Application.Resources>
 
    </Application.Resources>
</Application>

App.xaml.cs

using System;
using System.IO;
using System.Net;
using System.Security;
using System.Security.Permissions;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Navigation;
 
namespace VistaCatalog
{
    public partial class App : System.Windows.Application
    {
        protected override void OnLoadCompleted(NavigationEventArgs e)
        {
            // Set the Window title.
            this.MainWindow.Title = "Vista Catalog";
 
            // Get the window to take the size of its content, and then allow
            // it to be set by the user, and have the content take the size
            // of the window.
            if (!this.IsWebBrowserApplication)
            {
                this.MainWindow.SizeToContent = SizeToContent.WidthAndHeight;
                this.MainWindow.SizeToContent = SizeToContent.Manual;
            }
 
            FrameworkElement root = this.MainWindow.Content as FrameworkElement;
            if (root != null)
            {
                root.Height = double.NaN;
                root.Width = double.NaN;
 
                root.Focus();
            }
        }
 
        private bool IsWebBrowserApplication
        {
            get
            {
                try
                {
                    PermissionSet testSet = new PermissionSet(PermissionState.None);
                    testSet.AddPermission(new UIPermission(UIPermissionWindow.AllWindows));
                    testSet.Assert();
 
                    return false;
                }
                catch (SecurityException)
                {
                    return true;
                }
            }
        }
    }
}

Window1.xaml

<Grid x:Class="VistaCatalog.Grid1" ShowGridLines="False"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Width="640" Height="480" Loaded="OnLoaded">
 
    <Grid.Resources>
 
        <XmlDataProvider x:Key="ProductDataSource" Source="data.xml"/>
 
        <DataTemplate x:Key="ProductItemTemplate">
            <StackPanel>
                <TextBlock Text="{Binding Mode=OneWay, XPath=Name}"/>
            </StackPanel>
        </DataTemplate>
 
        <!-- ************************ -->
        <!-- ROTATING LIGHT ANIMATION -->
        <!-- ************************ -->
        <Storyboard x:Key="OnLoaded">
            <!-- Path of the light. Do not change it manually. It is designed using the Interactive Designer. -->
            <DoubleAnimationUsingPath BeginTime="00:00:00" 
                                      RepeatBehavior="Forever" 
                                      Duration="00:00:10" 
                                      Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[5].(TranslateTransform.X)" 
                                      Storyboard.TargetName="Ellipse" Source="X">
                <DoubleAnimationUsingPath.PathGeometry>
                    <PathGeometry>
                        <PathGeometry.Figures>
                            <PathFigureCollection>
                                <PathFigure StartPoint="572.5,1.25" IsClosed="True">
                                    <BezierSegment IsStroked="True" IsSmoothJoin="True" Point1="572.5,31.6256612406936" Point2="307.788358337457,56.25" Point3="-18.75,56.25"/>
                                    <BezierSegment IsStroked="True" IsSmoothJoin="True" Point1="-345.288358337457,56.25" Point2="-610,31.6256612406936" Point3="-610,1.25"/>
                                    <BezierSegment IsStroked="True" IsSmoothJoin="True" Point1="-610,-29.1256612406936" Point2="-345.288358337457,-53.75" Point3="-18.75,-53.75"/>
                                    <BezierSegment IsStroked="True" IsSmoothJoin="True" Point1="307.788358337457,-53.75" Point2="572.5,-29.1256612406936" Point3="572.5,1.25"/>
                                </PathFigure>
                            </PathFigureCollection>
                        </PathGeometry.Figures>
                    </PathGeometry>
                </DoubleAnimationUsingPath.PathGeometry>
            </DoubleAnimationUsingPath>
            <DoubleAnimationUsingPath BeginTime="00:00:00" RepeatBehavior="Forever" Duration="00:00:10" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[5].(TranslateTransform.Y)" Storyboard.TargetName="Ellipse" Source="Y">
                <DoubleAnimationUsingPath.PathGeometry>
                    <PathGeometry>
                        <PathGeometry.Figures>
                            <PathFigureCollection>
                                <PathFigure StartPoint="572.5,1.25" IsClosed="True">
                                    <BezierSegment IsStroked="True" IsSmoothJoin="True" Point1="572.5,31.6256612406936" Point2="307.788358337457,56.25" Point3="-18.75,56.25"/>
                                    <BezierSegment IsStroked="True" IsSmoothJoin="True" Point1="-345.288358337457,56.25" Point2="-610,31.6256612406936" Point3="-610,1.25"/>
                                    <BezierSegment IsStroked="True" IsSmoothJoin="True" Point1="-610,-29.1256612406936" Point2="-345.288358337457,-53.75" Point3="-18.75,-53.75"/>
                                    <BezierSegment IsStroked="True" IsSmoothJoin="True" Point1="307.788358337457,-53.75" Point2="572.5,-29.1256612406936" Point3="572.5,1.25"/>
                                </PathFigure>
                            </PathFigureCollection>
                        </PathGeometry.Figures>
                    </PathGeometry>
                </DoubleAnimationUsingPath.PathGeometry>
            </DoubleAnimationUsingPath>
            <!-- MasterMirror -->
            <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" RepeatBehavior="Forever" Storyboard.TargetProperty="(UIElement.OpacityMask).(GradientBrush.GradientStops)[0].(GradientStop.Offset)" Storyboard.TargetName="MasterMirror">
                <SplineDoubleKeyFrame Value="0.53" KeyTime="00:00:00" />
                <SplineDoubleKeyFrame Value="0.25" KeyTime="00:00:02" />
                <SplineDoubleKeyFrame Value="0.53" KeyTime="00:00:05" />
                <SplineDoubleKeyFrame Value="0.74" KeyTime="00:00:08" />
                <SplineDoubleKeyFrame Value="0.53" KeyTime="00:00:10" />
            </DoubleAnimationUsingKeyFrames>
            <!-- DetailMirror -->
            <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" RepeatBehavior="Forever" Storyboard.TargetProperty="(UIElement.OpacityMask).(GradientBrush.GradientStops)[0].(GradientStop.Offset)" Storyboard.TargetName="DetailMirror">
                <SplineDoubleKeyFrame Value="0.53" KeyTime="00:00:00" />
                <SplineDoubleKeyFrame Value="0.28" KeyTime="00:00:02" />
                <SplineDoubleKeyFrame Value="0.53" KeyTime="00:00:05" />
                <SplineDoubleKeyFrame Value="0.77" KeyTime="00:00:08" />
                <SplineDoubleKeyFrame Value="0.53" KeyTime="00:00:10" />
            </DoubleAnimationUsingKeyFrames>
        </Storyboard>
 
        <!-- ************************ -->
        <!--  SELECT ITEM ANIMATION   -->
        <!-- ************************ -->
        <!-- All transformations take place at the same time. Their duration is 0.5 sec. -->
        <Storyboard x:Key="FlyIn">
            <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[1].(ScaleTransform.ScaleX)" Storyboard.TargetName="DetailVisual">
                <SplineDoubleKeyFrame KeySpline="0.5,0.5,0.5,0.5" Value="0.2" KeyTime="00:00:00"/>
                <SplineDoubleKeyFrame KeySpline="0.5,0.5,0.5,0.5" Value="1" KeyTime="00:00:00.5"/>
            </DoubleAnimationUsingKeyFrames>
            <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[1].(ScaleTransform.ScaleY)" Storyboard.TargetName="DetailVisual">
                <SplineDoubleKeyFrame KeySpline="0.5,0.5,0.5,0.5" Value="0.2" KeyTime="00:00:00"/>
                <SplineDoubleKeyFrame KeySpline="0.5,0.5,0.5,0.5" Value="1" KeyTime="00:00:00.5"/>
            </DoubleAnimationUsingKeyFrames>
            <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[4].(TranslateTransform.X)" Storyboard.TargetName="DetailVisual">
                <SplineDoubleKeyFrame KeySpline="0.5,0.5,0.5,0.5" Value="-300" KeyTime="00:00:00"/>
                <SplineDoubleKeyFrame KeySpline="0.5,0.5,0.5,0.5" Value="0" KeyTime="00:00:00.5"/>
            </DoubleAnimationUsingKeyFrames>
            <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[4].(TranslateTransform.Y)" Storyboard.TargetName="DetailVisual">
                <SplineDoubleKeyFrame KeySpline="0.5,0.5,0.5,0.5" Value="0" KeyTime="00:00:00"/>
                <SplineDoubleKeyFrame KeySpline="0.5,0.5,0.5,0.5" Value="0" KeyTime="00:00:00.5"/>
            </DoubleAnimationUsingKeyFrames>
            <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(RotateTransform.Angle)" Storyboard.TargetName="DetailVisual">
                <SplineDoubleKeyFrame KeySpline="0.5,0.5,0.5,0.5" Value="-90" KeyTime="00:00:00"/>
                <SplineDoubleKeyFrame KeySpline="0.5,0.5,0.5,0.5" Value="0" KeyTime="00:00:00.5"/>
            </DoubleAnimationUsingKeyFrames>
        </Storyboard>
 
        <ControlTemplate x:Key="PanelTemplate" TargetType="{x:Type Control}">
            <Grid>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition/>
                </Grid.ColumnDefinitions>
                <Grid.RowDefinitions>
                    <RowDefinition/>
                </Grid.RowDefinitions>
 
                <Rectangle RadiusX="15" RadiusY="15" Stroke="{x:Null}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="0,0,0,0" Width="Auto" Height="Auto"> 
                    <Rectangle.Fill>
                        <LinearGradientBrush StartPoint="0,0" EndPoint="1,0">
                            <LinearGradientBrush.RelativeTransform>
                                <TransformGroup>
                                    <TranslateTransform X="0" Y="0"/>
                                    <ScaleTransform ScaleX="1" ScaleY="1"/>
                                    <SkewTransform AngleX="0" AngleY="0"/>
                                    <RotateTransform Angle="90"/>
                                    <TranslateTransform X="0" Y="0"/>
                                </TransformGroup>
                            </LinearGradientBrush.RelativeTransform>
                            <LinearGradientBrush.GradientStops>
                                <GradientStopCollection>
                                    <GradientStop Color="#FF000000" Offset="0"/>
                                    <GradientStop Color="sc#1, 0.015, 0, 0.6" Offset="1"/>
                                </GradientStopCollection>
                            </LinearGradientBrush.GradientStops>
                        </LinearGradientBrush>
                    </Rectangle.Fill>
                </Rectangle>
 
                <Rectangle RadiusX="10" RadiusY="13" VerticalAlignment="Top" Margin="6,6,6,0" Height="16" HorizontalAlignment="Stretch" Width="Auto">
                    <Rectangle.Fill>
                        <LinearGradientBrush StartPoint="0,0" EndPoint="1,0">
                            <LinearGradientBrush.RelativeTransform>
                                <TransformGroup>
                                    <TranslateTransform X="0" Y="0"/>
                                    <ScaleTransform ScaleX="1" ScaleY="1"/>
                                    <SkewTransform AngleX="0" AngleY="0"/>
                                    <RotateTransform Angle="90"/>
                                    <TranslateTransform X="0" Y="0"/>
                                </TransformGroup>
                            </LinearGradientBrush.RelativeTransform>
                            <LinearGradientBrush.GradientStops>
                                <GradientStopCollection>
                                    <GradientStop Color="#FFFFFFFF" Offset="0"/>
                                    <GradientStop Color="sc#0, 1, 1, 1" Offset="0.71"/>
                                </GradientStopCollection>
                            </LinearGradientBrush.GradientStops>
                        </LinearGradientBrush>
                    </Rectangle.Fill>
                </Rectangle>
 
            </Grid>
        </ControlTemplate>
    </Grid.Resources>
 
    <!-- ************************ -->
    <!--     WINDOW BACKGROUND    -->
    <!-- ************************ -->
    <Grid.Background>
        <LinearGradientBrush StartPoint="0,0" EndPoint="1,0">
            <LinearGradientBrush.RelativeTransform>
                <TransformGroup>
                    <RotateTransform Angle="90"/>
                </TransformGroup>
            </LinearGradientBrush.RelativeTransform>
            <LinearGradientBrush.GradientStops>
                <GradientStopCollection>
                    <GradientStop Color="#FF000000" Offset="0"/>
                    <GradientStop Color="sc#1, 0.2, 0.2, 0.2" Offset="0.5"/>
                    <GradientStop Color="#FF000000" Offset="1"/>
                </GradientStopCollection>
            </LinearGradientBrush.GradientStops>
        </LinearGradientBrush>
    </Grid.Background>
 
    <!-- ************************ -->
    <!--        TRIGGERS          -->
    <!-- ************************ -->
    <Grid.Triggers>
        <EventTrigger RoutedEvent="FrameworkElement.Loaded">
            <BeginStoryboard x:Name="OnLoaded_BeginStoryboard" Storyboard="{DynamicResource OnLoaded}"/>
        </EventTrigger>
        <EventTrigger RoutedEvent="Selector.SelectionChanged" SourceName="MasterList">
            <BeginStoryboard x:Name="FlyIn_BeginStoryboard" Storyboard="{StaticResource FlyIn}"/>
        </EventTrigger>
    </Grid.Triggers>
 
    <!-- ************************ -->
    <!--     MAIN LAYOUT GRID     -->
    <!-- ************************ -->
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="0.4*"/>
        <ColumnDefinition Width="0.6*"/>
    </Grid.ColumnDefinitions>
    <Grid.RowDefinitions>
        <RowDefinition Height="0.6*"/>
        <RowDefinition Height="0.4*"/>
    </Grid.RowDefinitions>
 
    <!-- ************************ -->
    <!--     ROTATING LIGHT       -->
    <!-- ************************ -->
    <Ellipse x:Name="Ellipse" Stroke="{x:Null}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" 
             Margin="0,230,10,185" Width="Auto" Height="Auto" Grid.RowSpan="2" Grid.ColumnSpan="2">
        <Ellipse.RenderTransform>
            <TransformGroup>
                <TranslateTransform X="0" Y="0"/>
                <ScaleTransform ScaleX="1" ScaleY="1"/>
                <SkewTransform AngleX="0" AngleY="0"/>
                <RotateTransform Angle="0"/>
                <TranslateTransform X="0" Y="0"/>
                <TranslateTransform X="0" Y="0"/>
            </TransformGroup>
        </Ellipse.RenderTransform>
        <Ellipse.Fill>
            <RadialGradientBrush>
                <RadialGradientBrush.GradientStops>
                    <GradientStopCollection>
                        <GradientStop Color="#FFFFFFFF" Offset="0"/>
                        <GradientStop Color="sc#0, 0, 0, 0" Offset="1"/>
                    </GradientStopCollection>
                </RadialGradientBrush.GradientStops>
            </RadialGradientBrush>
        </Ellipse.Fill>
    </Ellipse>
 
    <!-- ************************ -->
    <!--        MASTER GRID       -->
    <!-- ************************ -->
    <Grid x:Name="MasterGrid" Margin="50,50,10,1" HorizontalAlignment="Stretch" Width="Auto">
 
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition/>
        </Grid.RowDefinitions>
 
        <Grid x:Name="MasterVisual" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" 
              Margin="0,0,0,0" Width="Auto" Height="Auto">
 
            <Grid.ColumnDefinitions>
                <ColumnDefinition/>
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
                <RowDefinition/>
            </Grid.RowDefinitions>
 
            <!-- Visual appearance of the left panel -->
            <Control x:Name="Control" Margin="0,0,0,0" Template="{DynamicResource PanelTemplate}" />
 
            <!-- ListBox containing the list of products -->
            <ListBox x:Name="MasterList" Padding="4,20,4,1" Margin="0,5,0,0" 
                     Background="{x:Null}" BorderBrush="{x:Null}" Foreground="#FFFFFFFF" FontFamily="Verdana" 
                     ItemsSource="{Binding Mode=Default, Source={StaticResource ProductDataSource}, XPath=/Sections/Extras/Extra}" 
                     ItemTemplate="{DynamicResource ProductItemTemplate}" SelectedIndex="0" />
        </Grid>
    </Grid>
 
    <!-- ************************ -->
    <!--        DETAIL GRID       -->
    <!-- ************************ -->
    <Grid x:Name="DetailGrid" Margin="12,50,70,1" Grid.Column="1" 
          HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Width="Auto" Height="Auto">
 
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition/>
        </Grid.RowDefinitions>
 
        <!-- Important RenderTransformOrigin -->
        <Grid x:Name="DetailVisual" Margin="0,0,0,0"
              RenderTransformOrigin="0.5,0.5" 
              DataContext="{Binding SelectedItem, ElementName=MasterList, Mode=OneWay}">
 
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="0.6*"/>
                <ColumnDefinition Width="0.4*"/>
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
                <RowDefinition Height="40"/>
                <RowDefinition/>
            </Grid.RowDefinitions>
 
            <Grid.RenderTransform>
                <TransformGroup>
                    <TranslateTransform X="0" Y="0"/>
                    <ScaleTransform ScaleX="1" ScaleY="1"/>
                    <SkewTransform AngleX="0" AngleY="0"/>
                    <RotateTransform Angle="0"/>
                    <TranslateTransform X="0" Y="0"/>
                </TransformGroup>
            </Grid.RenderTransform>
 
            <!-- Detail panel background -->
            <Control x:Name="Control1" Margin="0,0,0,0" Grid.ColumnSpan="2" Grid.RowSpan="2"
                     Template="{DynamicResource PanelTemplate}"/>
 
            <!-- Product Name-->
            <TextBlock Margin="0,12,0,0" FontSize="18" 
                       HorizontalAlignment="Stretch" VerticalAlignment="Stretch" 
                       Height="40" Width="Auto" TextWrapping="Wrap" Grid.ColumnSpan="2" 
                       Text="{Binding Mode=OneWay, XPath=Name}" 
                       TextAlignment="Center" Foreground="#FFFFFFFF" FontFamily="Verdana" />
 
            <StackPanel Grid.Row="1">
 
                <!-- Label Description -->
                <Label Content="Description:" Margin="10,10,0,0" FontWeight="Bold" Height="Auto"
                       Foreground="#FFFFFFFF" FontFamily="Verdana"/>
 
                <!-- Description -->
                <TextBlock Margin="20,0,0,0" Text="{Binding Mode=OneWay, XPath=Description}"
                           TextWrapping="Wrap" Height="Auto" Foreground="#FFFFFFFF" FontFamily="Verdana"/>
 
                <!-- Label Price -->
                <Label Content="Price:" Margin="10,10,0,0" FontWeight="Bold" Height="Auto" Width="41"
                       HorizontalAlignment="Left" Foreground="#FFFFFFFF" FontFamily="Verdana"/>
 
                <!-- Price -->
                <TextBlock Margin="20,0,0,0" VerticalAlignment="Bottom" Height="Auto" 
                           Text="{Binding Mode=OneWay, XPath=Price}" TextWrapping="Wrap" 
                           Foreground="#FFFFFFFF" FontFamily="Verdana"></TextBlock>
 
            </StackPanel>
 
            <Image x:Name="Image" Grid.Row="1" Grid.Column="1" HorizontalAlignment="Stretch" 
                   Source="{Binding Mode=OneWay, XPath=Image}" 
                   VerticalAlignment="Stretch" Margin="0,49,9,8" Width="Auto"/>
        </Grid>
    </Grid>
 
    <!-- ************************ -->
    <!--      MASTER MIRROR       -->
    <!-- ************************ -->
    <Rectangle x:Name="MasterMirror" RadiusX="13" RadiusY="13" 
               HorizontalAlignment="Stretch" VerticalAlignment="Stretch" 
               Margin="50,0,10,0" Width="Auto" 
               RenderTransformOrigin="0.5,0.5" Grid.Row="1">
 
        <!-- Put "upside-down" -->
        <Rectangle.RenderTransform>
            <TransformGroup>
                <ScaleTransform ScaleX="1" ScaleY="-1"/>
            </TransformGroup>
        </Rectangle.RenderTransform>
 
        <Rectangle.Fill>
            <LinearGradientBrush StartPoint="0,0.5" EndPoint="1,0.5">
                <LinearGradientBrush.RelativeTransform>
                    <TransformGroup>
                        <RotateTransform Angle="90"/>
                    </TransformGroup>
                </LinearGradientBrush.RelativeTransform>
                <LinearGradientBrush.GradientStops>
                    <GradientStopCollection>
                        <GradientStop Color="#FFFFFFFF" Offset="0"/>
                        <GradientStop Color="sc#0, 1, 1, 1" Offset="0.71"/>
                    </GradientStopCollection>
                </LinearGradientBrush.GradientStops>
            </LinearGradientBrush>
        </Rectangle.Fill>
 
        <Rectangle.OpacityMask>
            <LinearGradientBrush StartPoint="0,0.5" EndPoint="1,0.5">
                <LinearGradientBrush.RelativeTransform>
                    <TransformGroup>
                        <RotateTransform Angle="90"/>
                    </TransformGroup>
                </LinearGradientBrush.RelativeTransform>
                <LinearGradientBrush.GradientStops>
                    <GradientStopCollection>
                        <GradientStop Color="sc#0, 0, 0, 0" Offset="0.53"/>
                        <GradientStop Color="sc#0.5, 0, 0, 0" Offset="1"/>
                    </GradientStopCollection>
                </LinearGradientBrush.GradientStops>
            </LinearGradientBrush>
        </Rectangle.OpacityMask>
    </Rectangle>
 
    <!-- ************************ -->
    <!--      DETAIL MIRROR       -->
    <!-- ************************ -->
    <Rectangle x:Name="DetailMirror" RadiusX="13" RadiusY="13" 
               HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
               Margin="12,0,70,1" Width="Auto" 
               RenderTransformOrigin="0.5,0.5" Grid.Row="1" Grid.Column="1">
 
        <Rectangle.RenderTransform>
            <TransformGroup>
                <ScaleTransform ScaleX="1" ScaleY="-1"/>
            </TransformGroup>
        </Rectangle.RenderTransform>
 
        <Rectangle.Fill>
            <LinearGradientBrush StartPoint="0,0.5" EndPoint="1,0.5">
                <LinearGradientBrush.RelativeTransform>
                    <TransformGroup>
                        <TranslateTransform X="-0.5" Y="-0.5"/>
                        <ScaleTransform ScaleX="0.984872454010245" ScaleY="0.984872454010245"/>
                        <SkewTransform AngleX="0" AngleY="0"/>
                        <RotateTransform Angle="90.34223031142686"/>
                        <TranslateTransform X="0.5" Y="0.5"/>
                        <TranslateTransform X="-0.0013788390464789251" Y="0.00076285714239791337"/>
                    </TransformGroup>
                </LinearGradientBrush.RelativeTransform>
                <LinearGradientBrush.GradientStops>
                    <GradientStopCollection>
                        <GradientStop Color="#FFFFFFFF" Offset="0"/>
                        <GradientStop Color="sc#0, 1, 1, 1" Offset="0.7142857142857143"/>
                    </GradientStopCollection>
                </LinearGradientBrush.GradientStops>
            </LinearGradientBrush>
        </Rectangle.Fill>
 
        <Rectangle.OpacityMask>
            <LinearGradientBrush StartPoint="0,0.5" EndPoint="1,0.5">
                <LinearGradientBrush.RelativeTransform>
                    <TransformGroup>
                        <TranslateTransform X="-0.5" Y="-0.5"/>
                        <ScaleTransform ScaleX="0.984872454010245" ScaleY="0.984872454010245"/>
                        <SkewTransform AngleX="0" AngleY="0"/>
                        <RotateTransform Angle="90.34223031142686"/>
                        <TranslateTransform X="0.5" Y="0.5"/>
                        <TranslateTransform X="-0.0013788390464789251" Y="0.00076285714239791337"/>
                    </TransformGroup>
                </LinearGradientBrush.RelativeTransform>
                <LinearGradientBrush.GradientStops>
                    <GradientStopCollection>
                        <GradientStop Color="sc#0, 0, 0, 0" Offset="0.53246753246753242"/>
                        <GradientStop Color="sc#0.5, 0, 0, 0" Offset="1"/>
                    </GradientStopCollection>
                </LinearGradientBrush.GradientStops>
            </LinearGradientBrush>
        </Rectangle.OpacityMask>
    </Rectangle>
 
</Grid>

Window1.xaml.cs

using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
 
namespace VistaCatalog
{
    public partial class Grid1
    {
        private void OnLoaded(object sender, RoutedEventArgs e)
        {
            MasterMirror.Fill = new VisualBrush(MasterVisual);
            DetailMirror.Fill = new VisualBrush(DetailVisual);
        }
    }
}

data.xml

<?xml version="1.0" encoding="UTF-8"?>
 
<Sections>
	<Extras>
		<Extra>
			<Name>VPR-1000 Protect Case</Name>
			<Image>Images/extra01.png</Image>
			<Description>High resistent polymers and aluminum extracts that will protect your widget for life. Fire, Snow, Water or Dust... This guy takes it all.</Description>
			<Price>$87.00</Price>
		</Extra>
		<Extra>
			<Name>Headphones</Name>
			<Image>Images/extra02.png</Image>
			<Description>Add some high quality audio to your video experience. With these comfortable headphones you'll feel like sound comes out of your brain.</Description>
			<Price>$19.20</Price>
		</Extra>
		<Extra>
			<Name>Antenna</Name>
			<Image>Images/extra03.png</Image>
			<Description>Get connected to local wireless networks and download streaming media directly to your VPR-1000</Description>			
			<Price>$38.45</Price>
		</Extra>
		<Extra>
			<Name>2TB Hard Drive</Name>
			<Image>Images/extra04.png</Image>
			<Description>If you are about to travel then you need extra storage space.  With this 2TB drive you'll finally be able to take thousands of hours of entertainment with you.</Description>		
			<Price>$75.90</Price>
		</Extra>
		<Extra>
			<Name>1GB Card</Name>
			<Image>Images/extra05.png</Image>
			<Description>Take more pictures and save them. Plug your card in your VPR-1000 or any other compatible device and browse through your pictures.</Description>
			<Price>$29.90</Price>
		</Extra>
		<Extra>
			<Name>Tripod</Name>
			<Image>Images/extra06.png</Image>
			<Description>Take your VPR-1000 and use it everywhere. Flat or curved, soft or hard surfaces, this tripod is ready to do the job.</Description>		
			<Price>$12.35</Price>
		</Extra>
		<Extra>
			<Name>Data Port</Name>
			<Image>Images/extra07.png</Image>
			<Description>Expand you connectivity possibilities using this super fast data port. Sends and receives data at high speed using a powerful encoder.</Description>			
			<Price>$25.79</Price>
		</Extra>
		<Extra>
			<Name>Bluetooth module</Name>
			<Image>Images/extra08.png</Image>
			<Description>Be part of this wide spread technology. Connect to any other widget or device using the BT module.</Description>		
			<Price>$32.90</Price>
		</Extra>
 
	</Extras>
 
</Sections>

Custom Option Group

<Window x:Class="TestApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Fancy Option Group" SizeToContent="WidthAndHeight">
 
    <Window.Resources>
        <DrawingBrush x:Key="RadioButtonBackgroundBrush" Viewbox="0,0,20,20" 
                      ViewboxUnits="Absolute" Stretch="Uniform">
            <DrawingBrush.Drawing>
                <DrawingGroup>
                    <DrawingGroup.Children>
                        <GeometryDrawing Geometry='M 9.85779,1.72414C 14.3395,1.72414 17.9725,5.35411 17.9725,9.8319C 17.9725,14.3097 14.3394,17.9397 9.85779,17.9397C 5.37614,17.9397 1.74304,14.3097 1.74304,9.8319C 1.74304,5.35411 5.37614,1.72414 9.85779,1.72414'>
                            <GeometryDrawing.Brush>
                                <!-- Button Glow -->
                                <RadialGradientBrush MappingMode='Absolute' GradientOrigin='9.72537,15.4623' Center='9.72537,15.4623' RadiusX='12.1158' RadiusY='12.1053'>
                                    <GradientBrush.GradientStops>
                                        <GradientStopCollection>
                                            <GradientStop Color='#FF3C00FF' Offset='0'/>
                                            <GradientStop Color='#FF00196B' Offset='1'/>
                                        </GradientStopCollection>
                                    </GradientBrush.GradientStops>
                                </RadialGradientBrush>
                            </GeometryDrawing.Brush>
                        </GeometryDrawing>
 
                        <GeometryDrawing Geometry='M 9.89178,2.46702C 12.9144,2.46702 15.3648,4.19728 15.3648,6.33165C 15.3648,8.46603 12.9144,10.1963 9.89178,10.1963C 6.86911,10.1963 4.41875,8.46603 4.41875,6.33165C 4.41875,4.19728 6.86912,2.46702 9.89178,2.46702'>
                            <GeometryDrawing.Brush>
                                <!-- Button Highlight -->
                                <LinearGradientBrush MappingMode='Absolute' 
                                    StartPoint='9.70277,1.94937' 
                                    EndPoint='9.5379,10.226'>
                                    <GradientBrush.GradientStops>
                                        <GradientStopCollection>
                                            <GradientStop Color='#FFFFFFFF' Offset='0'/>
                                            <GradientStop Color='#00FFFFFF' Offset='1'/>
                                        </GradientStopCollection>
                                    </GradientBrush.GradientStops>
                                </LinearGradientBrush>
                            </GeometryDrawing.Brush>
                        </GeometryDrawing>
 
                        <GeometryDrawing Geometry='M 9.85779,1.61153C 14.3395,1.61153 17.9725,5.2415 17.9725,9.71929C 17.9725,14.1971 14.3394,17.827 9.85779,17.827C 5.37614,17.827 1.74304,14.1971 1.74304,9.71929C 1.74304,5.2415 5.37614,1.61153 9.85779,1.61153'>
                            <GeometryDrawing.Pen>
                                <Pen LineJoin='Round'>
                                    <Pen.Brush>
                                        <!-- White To black circle around the button -->
                                        <LinearGradientBrush MappingMode='Absolute' 
                                            StartPoint='9.72537,15.3497' 
                                            EndPoint='9.78172,3.24435'>
                                            <GradientBrush.GradientStops>
                                                <GradientStopCollection>
                                                    <GradientStop Color='#FFE0E0E0' Offset='0'/>
                                                    <GradientStop Color='#FF3D3D3D' Offset='1'/>
                                                </GradientStopCollection>
                                            </GradientBrush.GradientStops>
                                        </LinearGradientBrush>
                                    </Pen.Brush>
                                </Pen>
                            </GeometryDrawing.Pen>
                        </GeometryDrawing>
 
                    </DrawingGroup.Children>
                </DrawingGroup>
            </DrawingBrush.Drawing>
        </DrawingBrush>
 
        <DrawingBrush x:Key="RadioButtonTickBrush" Viewbox="0,0,20,20" 
            ViewboxUnits="Absolute" Stretch="Uniform">
            <DrawingBrush.Drawing>
                <DrawingGroup>
                    <DrawingGroup.Children>
                        <GeometryDrawing Brush='#6E000000' Geometry='M 9.45246,12.2559C 9.9151,11.279 10.4664,10.2064 11.1065,9.03795C 11.7466,7.8695 12.4707,6.70428 13.2787,5.54225C 14.0867,4.38022 14.676,3.60925 15.0468,3.22939C 15.4175,2.8495 15.7312,2.54782 15.9879,2.32435C 16.2445,2.10088 16.6992,1.93807 17.352,1.83591C 18.0047,1.73376 18.4991,1.68268 18.8349,1.68268C 19.0567,1.68268 19.2389,1.75132 19.3815,1.88859C 19.5241,2.02586 19.5954,2.20623 19.5954,2.4297C 19.5954,2.60205 19.5542,2.75053 19.4718,2.87504C 19.3894,2.99954 19.2183,3.1863 18.9585,3.4353C 17.7101,4.59095 16.4109,6.23822 15.061,8.37712C 13.7112,10.516 12.6402,12.5879 11.848,14.5927C 11.4741,15.5121 11.2333,16.0516 11.1255,16.2112C 11.0178,16.3709 10.853,16.5193 10.6312,16.6566C 10.4094,16.7939 9.95312,16.8625 9.26234,16.8625C 8.71734,16.8625 8.3688,16.829 8.21671,16.7619C 8.06461,16.6949 7.94735,16.6231 7.86497,16.5464C 7.78258,16.4698 7.5481,16.1378 7.16154,15.5504C 6.76227,14.9311 6.1824,14.2 5.42191,13.3573C 4.98465,12.872 4.76603,12.4666 4.76603,12.1409C 4.76603,11.6685 5.01476,11.2567 5.51224,10.9055C 6.0097,10.5543 6.44856,10.3788 6.8288,10.3788C 7.24708,10.3788 7.69386,10.5495 8.16916,10.8911C 8.64446,11.2327 9.07222,11.6876 9.45246,12.2559'/>
                        <GeometryDrawing Geometry='M 8.75843,11.2395C 9.22107,10.2626 9.77242,9.18997 10.4125,8.02156C 11.0526,6.85311 11.7766,5.68789 12.5846,4.52586C 13.3926,3.36383 13.982,2.59287 14.3527,2.21301C 14.7235,1.83311 15.0372,1.53143 15.2938,1.30796C 15.5505,1.0845 16.0052,0.921684 16.6579,0.819527C 17.3107,0.717371 17.805,0.666292 18.1409,0.666292C 18.3627,0.666292 18.5449,0.734928 18.6875,0.872202C 18.8301,1.00947 18.9014,1.18984 18.9014,1.41331C 18.9014,1.58567 18.8602,1.73415 18.7778,1.85865C 18.6954,1.98315 18.5243,2.16991 18.2644,2.41891C 17.016,3.57456 15.7169,5.22183 14.367,7.36073C 13.0171,9.49964 11.9461,11.5715 11.1539,13.5763C 10.7801,14.4957 10.5392,15.0352 10.4315,15.1949C 10.3238,15.3545 10.159,15.5029 9.93721,15.6402C 9.71538,15.7775 9.25909,15.8461 8.56831,15.8461C 8.02331,15.8461 7.67477,15.8126 7.52268,15.7455C 7.37058,15.6785 7.25332,15.6067 7.17094,15.5301C 7.08855,15.4534 6.85407,15.1214 6.46751,14.534C 6.06824,13.9147 5.48836,13.1837 4.72788,12.3409C 4.29062,11.8556 4.072,11.4502 4.072,11.1246C 4.072,10.6521 4.32073,10.2403 4.81821,9.88911C 5.31567,9.53794 5.75452,9.36236 6.13477,9.36236C 6.55305,9.36236 6.99983,9.53316 7.47513,9.87474C 7.95043,10.2163 8.37819,10.6712 8.75843,11.2395'>
                            <GeometryDrawing.Brush>
                                <!-- Yellow & Orange Radial -->
                                <RadialGradientBrush MappingMode='Absolute' 
                                    GradientOrigin='11.4867,8.2562' 
                                    Center='11.4867,8.2562' 
                                    RadiusX='8' RadiusY='12'>
                                    <GradientBrush.GradientStops>
                                        <GradientStopCollection>
                                            <GradientStop Color='#FFFFFF00' Offset='0.190002'/>
                                            <GradientStop Color='#FFFF7319' Offset='1'/>
                                        </GradientStopCollection>
                                    </GradientBrush.GradientStops>
                                </RadialGradientBrush>
                            </GeometryDrawing.Brush>
                        </GeometryDrawing>
                    </DrawingGroup.Children>
                </DrawingGroup>
            </DrawingBrush.Drawing>
        </DrawingBrush>        
    </Window.Resources>
 
    <Grid>
        <!-- Magnify option groups -->
        <Grid.LayoutTransform>
            <ScaleTransform ScaleX="3" ScaleY="3" />
        </Grid.LayoutTransform>
 
        <!-- Two colums - one for each option group -->
        <Grid.ColumnDefinitions>
            <ColumnDefinition />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
 
        <Grid.Resources>
 
            <!-- Option group style -->
            <Style x:Key="GroupBoxStyle">
                <Setter Property="ContentControl.Template">
                    <Setter.Value>
                        <ControlTemplate>
                            <Grid>
                                <Rectangle Stroke="Blue" RadiusX="10" RadiusY="10" Margin="9" />
                                <Grid Background="White" 
                                    VerticalAlignment="Top" 
                                    HorizontalAlignment="Left" 
                                    Margin="20,2">
                                    <ContentPresenter Content="{TemplateBinding HeaderedContentControl.Header}" />
                                </Grid>
                                <ContentPresenter Margin="30" 
                                    Content="{TemplateBinding HeaderedContentControl.Content}" />
                            </Grid>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
            </Style>
 
            <!-- Radio button style -->
            <Style TargetType="{x:Type RadioButton}">
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate TargetType="{x:Type RadioButton}">
                            <!-- Background and tick together with a placeholder for radio button caption -->
                            <StackPanel Orientation="Horizontal">
                                <Grid>
                                    <!-- Both rectangles go to the same cell -->
                                    <Rectangle Width="15" Height="15" 
                                        Fill="{StaticResource RadioButtonBackgroundBrush}" />
                                    <Rectangle x:Name="TickElement" Visibility="Hidden" 
                                        Width="15" Height="15" 
                                        Fill="{StaticResource RadioButtonTickBrush}" />
                                </Grid>
                                <ContentPresenter Content="{TemplateBinding Content}" 
                                                  VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
                                                  HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                                                  Margin="{TemplateBinding Padding}" />
                            </StackPanel>
 
                            <!-- Trigger to check/uncheck the radio buttons -->
                            <ControlTemplate.Triggers>
                                <Trigger Property="IsChecked" Value="True">
                                    <Setter Property="Rectangle.Visibility" 
                                        TargetName="TickElement" Value="Visible" />
                                </Trigger>
                            </ControlTemplate.Triggers>
                        </ControlTemplate>
 
                    </Setter.Value>
                </Setter>
            </Style>
 
        </Grid.Resources>
 
        <!-- First option group -->
        <HeaderedContentControl Style="{StaticResource GroupBoxStyle}" Header="Books" Grid.Column="0">
            <StackPanel>
                <RadioButton>C++ for Dogs</RadioButton>
                <RadioButton>C# for Cats</RadioButton>
            </StackPanel>
        </HeaderedContentControl>
 
        <!-- Second option group -->
        <HeaderedContentControl Style="{StaticResource GroupBoxStyle}" Header="Keywords" Grid.Column="1" >
            <StackPanel>
                <RadioButton>Klepa</RadioButton>
                <RadioButton>Bara</RadioButton>
                <RadioButton>Muka</RadioButton>
            </StackPanel>
        </HeaderedContentControl>
    </Grid>
</Window>

Radio button list created using ListBox

The binding expression for the RadioButton.IsChecked property uses Binding.RelativeSource to retrieve the value of the ListBoxItem.IsSelected property. Thanks to that when a radio button is selected, the corresponding LisBoxItem is also selected and vice-versa. The example utilizes the ItemContainerStyle property to set the template.

<Window x:Class="QuickTest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="200" Width="300">
 
    <Window.Resources>
        <Style x:Key="RadioButtonListStyle" TargetType="{x:Type ListBox}">
            <Setter Property="ItemContainerStyle">
                <Setter.Value>
                    <Style TargetType="{x:Type ListBoxItem}" >
                        <Setter Property="Margin" Value="5" />
                        <Setter Property="Template">
                            <Setter.Value>
                                <ControlTemplate TargetType="{x:Type ListBoxItem}">
                                    <!-- Retrieve the value of the ListBoxItem.IsSelected
                                         property using Binding.RelativeSource -->
                                    <!-- Set RadioButton.Focusable to false in order to prevent 
                                         tabbing to the current ListBoxItem and the RadioButton -->
                                    <RadioButton Focusable="False"
                                        IsChecked="{Binding Path=IsSelected,
                                            RelativeSource={RelativeSource TemplatedParent},
                                            Mode=TwoWay}">
                                        <!-- ContentPresenter represents the content of the ListBoxItem
                                             for example the ListBox.DisplayMemberPath property or
                                             a ListBox.ItemTemplate property -->
                                        <ContentPresenter></ContentPresenter>
                                    </RadioButton>
                                </ControlTemplate>
                            </Setter.Value>
                        </Setter>
                    </Style>
                </Setter.Value>
            </Setter>
        </Style>
    </Window.Resources>
 
    <ListBox x:Name="BooksListBox"
             Style="{StaticResource RadioButtonListStyle}" 
             DisplayMemberPath="Title">
    </ListBox>
</Window>
using System.Windows;
 
namespace QuickTest
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            Loaded += new RoutedEventHandler(MainWindow_Loaded);
        }
 
        protected void MainWindow_Loaded(object sender, RoutedEventArgs e)
        {
            // BookCatalog is a data provider class.
            BooksListBox.ItemsSource = BookCatalog.GetSampleBooks();
        }
    }
}

CompositeCollection

The CompositeCollection class is located at System.Windows.Data. It enables multiple collections and items to be displayed in a single list.

Example: Display a collection of strings as well as a ComboBoxItem in a ComboBox:

<Window x:Class="TestApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="TestApp"
        Width="300" Height="300">
    <StackPanel>
        <TextBlock Text="Programming languages:" Padding="5" />
        <ComboBox x:Name="cboLanguages"
                  SelectedIndex="0" Width="120" IsEditable="False" 
                  HorizontalAlignment="Left" Margin="5"
                  SelectionChanged="OnSelectionChanged">
            <ComboBox.ItemsSource>
                <CompositeCollection>
                    <ComboBoxItem IsSelected="True">[All Languages]</ComboBoxItem>
                    <CollectionContainer x:Name="LanguagesCollection" />
                </CompositeCollection>
            </ComboBox.ItemsSource>
        </ComboBox>
    </StackPanel>
</Window>
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Diagnostics;
 
namespace TestApp
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            LanguagesCollection.Collection = GetLanguages();
        }
 
        private void OnSelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            Debug.WriteLine("Selected value: " + ((ComboBox)sender).SelectedValue);
        }
 
        private IList<string> GetLanguages()
        {
            return new List<string>() { "C++", "C#", "JavaScript", "SQL" };
        }
    }
}

Sample output:

Selected value: C++
Selected value: JavaScript
Selected value: System.Windows.Controls.ComboBoxItem: [All Languages]
notes/wpf/examples.txt · Last modified: 2017/03/23 by leszek