News
Photos
Articles
Components
Applications
Kleinkunst

.NET - WPF/Silverlight treeviews and LINQ to SQL

I was trying to learn a little bit more about WPF (.NET 3.0) styles and data binding and using my knowledge about LINQ to SQL (.NET 3.5) at the same time. So this is a small tutorial on how to create nice looking treeviews with WPF which are populated with data using LINQ to SQL on the Northwind database. Of course databinding with POCO classes or entities from the Entity Framework will work in the same way.

Update October 2008: I updated this article to point out some Silverlight issues. When you use the TreeView control from the Silverlight Toolkit almost all these WPF examples can also be used in Silverlight.

Example 1 : Treeview with categories and subnodes for their products

  • LINQ to SQL : Fetching master-detail
  • WPF : 2-levels databinding with TreeView
  • WPF : Resources & templates
  • WPF : Styles
  • WPF : DataTrigger and conditional formatting

Example 2 : Treeview with countries, stockgroups and products

  • LINQ to SQL : Grouping
  • WPF : 3-levels databinding with TreeView
  • WPF : Resources & templates
  • WPF : Styles
  • WPF : DataTrigger and conditional formatting
  • WPF : Value converter to show language-dependent descriptions
  • WPF : Value converter to get resource bitmap

Example 3 : Reorganized treeview with countries and different items for USA & UK employees

  • LINQ to SQL : Inheritance
  • LINQ to SQL : Grouping
  • WPF : 2-levels databinding with TreeView
  • WPF : Resources & templates
  • WPF : Value converter to get bitmap from database
  • WPF : Styles
  • WPF : Image reflection

Screenshot example 2

Screenshot example 3

Structure of the solution

1) Start creating a new solution with the structure as described :

NortwindData (Class library)

  • NorthWind.dbml : LINQ to SQL Object Relational Diagram of Northwind SQL Server database.
  • Data.cs : Serveral classes to store LINQ queries results. DataProvider class which will retrieve data and pass it to the WindowTreeView window.

     

WpfDataBinding (WPF application)

  • Images : resource folder with PNG image files
  • WindowTreeView.xaml : XAML window with 3 treeview components.
  • WindowTreeView.xaml.cs : C# code to retrieve data and set ItemsSources of the treeviews. Several ValueConverter classes which are used in the XAML code.


Example 1A

Show a treeview with categories and subnodes for their products

  • LINQ to SQL : Fetching master-detail
  • WPF : 2-levels databinding with TreeView

First take a look at the LINQ to SQL Object Relational Diagram from the Northwind database. In Example 1 & 2 we will use the Product, Category and Supplier entities and their associations.

1) Open the Data.cs file in the NorthwindData project. Create a GetCategories_Products method in the DataProvider class which will return a collection of categories with their products.

There is a one-to-many association between the categories (parent) and the products (child). So if we retrieve the categories, we can have access to the collection of products. Because we like to fetch all data which are needed in one time, we need to set the LoadOptions property of the DataContext.

public class DataProvider
{
  public IEnumerable<Category> GetCategories_Products()
  {
    NorthWindDataContext dc = new NorthWindDataContext();
    DataLoadOptions options = new DataLoadOptions();
    options.LoadWith<Category>(p => p.Products);
    dc.LoadOptions = options;
 
    return dc.Categories;
  }
}
2) Open the WindowTreeView.cs file in the WPF application WPFDataBinding. Call this GetCategories_Products method in the constructor of partial Window class and pass the collection of business objects to the ItemSource property of the treeview.
public partial class WindowTreeView : Window
{
  public WindowTreeView()
  {
    InitializeComponent();
 
    NorthwindData.DataProvider dataProvider = new NorthwindData.DataProvider();
 
    treeView1.ItemsSource = dataProvider.GetCategories_Products();
  }
}

3) Switch to the XAML file and add a reference called northwind to the NorthwindData namespace and assembly.

4) One of the great features of WPF is databinding. It is very easy to link object properties to the user interface. So add a TreeView component and use 3 databindings in the HierarchicalDataTemplate (main nodes) and HierarchicalDataTemplate.ItemTemplate (sub nodes) parts.

Nesting HierarchicalDataTemplates will not work in Silverlight 2 due to a limitation in the Silverlight XAML parser. Take a look at example 1C how to work around this issue in Silverlight.

<Window x:Class="WpfDataBinding.WindowTreeView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 
    xmlns:northwind="clr-namespace:NorthwindData;assembly=NorthwindData"
    xmlns:local="clr-namespace:WpfDataBinding"
 
    Title="WindowTreeView" Height="200" Width="900" WindowState="Maximized">
 
    <TreeView Name="treeView1">
        <TreeView.ItemTemplate>
            <HierarchicalDataTemplate ItemsSource="{Binding Path=Products}">
                <TextBlock Text="{Binding Path=CategoryName}" />
                <HierarchicalDataTemplate.ItemTemplate>
                    <DataTemplate>
                       <TextBlock Text="{Binding Path=ProductName}" /> 
                    </DataTemplate>
                </HierarchicalDataTemplate.ItemTemplate>
            </HierarchicalDataTemplate>
        </TreeView.ItemTemplate>
    </TreeView>
</Window>
5) Run the WPF application to test the treeview.

Nesting a HierarchicalDataTemplate in Silverlight isn't possible. This is a known issue. You will get a "Message: one root element" parse error. This can be solved by using templates and resources like demonstrated at the bottom of following example.



Example 1B

1B : Same data as example 1A but extended layout for product items.

  • LINQ to SQL : Fetching master-detail
  • WPF : 2-levels databinding with TreeView
  • WPF : Resources & templates
  • WPF : Styles
  • WPF : DataTrigger and conditional formatting

1) Other powerful features of WPF/Silverlight are styles and templates. Let's improve the XAML code and separate and declare our templates as a Window resource. Create templates for each level of the tree. So start with a template for the Category class which uses Products as ItemsSource. In this example we will bind the template to our Northwind classes by using the DataType option.

<Window.Resources>
    <HierarchicalDataTemplate DataType="{x:Type northwind:Category}" ItemsSource="{Binding Path=Products}">
        <TextBlock Text="{Binding Path=CategoryName}"/>
    </HierarchicalDataTemplate>
</Window.Resources>

2) Create a new template for the Product class. Add a Grid and an extra TextBlock to display the UnitsInStock value.

<Window.Resources>
    <HierarchicalDataTemplate DataType="{x:Type northwind:Product}">
        <Grid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition></ColumnDefinition>
                <ColumnDefinition></ColumnDefinition>
            </Grid.ColumnDefinitions>
            <TextBlock Grid.Column="0" Text="{Binding Path=ProductName}" Padding="2" Width="200" />
            <TextBlock Grid.Column="1" Text="{Binding Path=UnitsInStock}" Padding="2" />
        </Grid>
   </HierarchicalDataTemplate>
3) Modify the TreeView.
<TreeView Name="treeView1">
</TreeView>

4) Create a Style called ProductFontStyle. We will use a DataTrigger to apply conditional formatting. When the product is Discontinued, the item will be displayed in gray and italic.

<Style x:Key="ProductFontStyle">
    <Setter Property="Control.Foreground" Value="Black" />
    <Style.Triggers>
        <DataTrigger Binding="{Binding Path=Discontinued}" Value="True">
            <Setter Property="Control.Foreground" Value="Gray" />
            <Setter Property="Control.FontStyle" Value="Italic" />
        </DataTrigger>
    </Style.Triggers>
</Style>
5) Use the StaticResource markup extension to refer to our ProductFontStyle in the TextBlocks of the HierarchicalDataTemplate.
<HierarchicalDataTemplate DataType="{x:Type northwind:Product}">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition></ColumnDefinition>
            <ColumnDefinition></ColumnDefinition>
        </Grid.ColumnDefinitions>
        <TextBlock Grid.Column="0" Text="{Binding Path=ProductName}" Padding="2" Width="200" 
            Style="{StaticResource ProductFontStyle}" />
        <TextBlock Grid.Column="1" Text="{Binding Path=UnitsInStock}" Padding="2" 
            Style="{StaticResource ProductFontStyle}" />
    </Grid>
</HierarchicalDataTemplate>

6) Run the application.

The HierarchicalDataTemplate in Silverlight does not provide a DataType property. So you have to specify a name and assign the ItemTemplate property.

<HierarchicalDataTemplate x:Name="ProductTemplate">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition></ColumnDefinition>
            <ColumnDefinition></ColumnDefinition>
        </Grid.ColumnDefinitions>
        <TextBlock Grid.Column="0" Text="{Binding Path=ProductName}" Padding="2" Width="200" 
            Style="{StaticResource ProductFontStyle}" />
        <TextBlock Grid.Column="1" Text="{Binding Path=UnitsInStock}" Padding="2" 
            Style="{StaticResource ProductFontStyle}" />
    </Grid>
</HierarchicalDataTemplate>
<TreeView Name="treeView1">
    <TreeView.ItemTemplate>
        <HierarchicalDataTemplate 
            ItemsSource="{Binding Path=Products}"
            ItemTemplate="{StaticResource ProductTemplate}">
            <TextBlock Text="{Binding Path=CategoryName}" />
        </HierarchicalDataTemplate>
    </TreeView.ItemTemplate>
</TreeView>

 

Example 1C

1C : Same data as example 1A & 1B but with different templates

  • WPF : Resources & templates

1) Another way to create templates is to give them a key value (x:Key) and refer to this resource in your treeview component. Because Silverlight doesn't support the x:Type markup extension (see example 1B), following technique is the way to go in a Silverlight 2 application. Of course this also works perfectly in WPF.

<Window.Resources>
    <HierarchicalDataTemplate x:Key="CategoryTemplate" ItemsSource="{Binding Path=Products}" ItemTemplate="{StaticResource ProductTemplate}">
        <TextBlock Text="{Binding Path=CategoryName}"/>
    </HierarchicalDataTemplate>
 
    <HierarchicalDataTemplate x:Key="ProductTemplate">
        <Grid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition></ColumnDefinition>
                <ColumnDefinition></ColumnDefinition>
            </Grid.ColumnDefinitions>
            <TextBlock Grid.Column="0" Text="{Binding Path=ProductName}" Padding="2" Width="200" />
            <TextBlock Grid.Column="1" Text="{Binding Path=UnitsInStock}" Padding="2" />
        </Grid>
   </HierarchicalDataTemplate>
<TreeView Name="treeView1" ItemTemplate="{StaticResource CategoryTemplate}">
</TreeView>


If you're using ViewModels and a M-V-VM pattern then you also have to specify the ItemSource of the TreeView and bind it with a collection property of your ViewModel class.

<TreeView Name="treeView1" ItemTemplate="{StaticResource CategoryTemplate}" ItemsSource="{Binding CategoryProducts}">
</TreeView>

Example 2A

2A : Show a treeview with supplier countries, stockgroups (small and large) and products.

  • LINQ to SQL : Grouping
  • WPF : 3-levels databinding with TreeView
  • WPF : Resources & templates

1) In this example we will not use the associations between entities to create hierarchical data. Instead we will use the GroupBy LINQ to SQL operator. The GroupBy operator will return a sequence of IGrouping values, one for each distinct key value that was encountered. Anonymous types can only be used locally, so if we want to pass the data of these LINQ queries around, we need to create our own hierarchical data transfer objects (DTO).

So create a custom SupplierCountryStockGroup and UnitsInStockGroup class in the Data.cs file in the NorthwindData project.

namespace NorthwindData
{
  public class SupplierCountryStockGroup
  {
    public string Country { get; set; }
    public IEnumerable<UnitsInStockGroup> StockGroups { get; set; }
  }
 
  public class UnitsInStockGroup
  {
    public string Country { get; set; }
    public bool StockGroup { get; set; }
    public IEnumerable<Product> Products { get; set; }
  }
}

2) Add a new method GetSupplierCountries_StockGroups_Products to the DataProvider class. This method will execute a LINQ to SQL query and return an IEnumerable of SupplierCountryStockGroup items. The method contains a main and sub query.

  • Sub query : Create 2 groups of products; stock smaller then 50 units and stock larger then 50 units. Save these grouped products in UnitsInStockGroup items.
  • Main query : Use the UnitsInStockGroup items and create a group for each supplier country. These results are stored in SupplierCountryStockGroup items.
namespace NorthwindData
{
  public class DataProvider
  {
    NorthWindDataContext dc;
 
    public DataProvider()
    {
      dc = new NorthWindDataContext();
    }
 
    public IEnumerable<SupplierCountryStockGroup> GetSupplierCountries_StockGroups_Products()
    {
      var GroupedProducts = from p in dc.Products.OrderBy(o => o.UnitsInStock)
                            where p.UnitsInStock != null
                            group p by new { Country = p.Supplier.Country, StockGroup = p.UnitsInStock > 50 } into gr
                            select new UnitsInStockGroup() 
                              { Country = gr.Key.Country, StockGroup = gr.Key.StockGroup, Products = gr };
 
      return from p in GroupedProducts
             group p by new { Country = p.Country } into gr
             select new SupplierCountryStockGroup() { Country = gr.Key.Country, StockGroups = gr };
    }
  }
}

3) Add two HierarchicalDataTemplates for level 1 (Country) and level 2 (StockGroup) in the XAML file. The Product template (from example 1) can be reused for level 3.

<HierarchicalDataTemplate DataType="{x:Type northwind:SupplierCountryStockGroup}" ItemsSource="{Binding Path=StockGroups}" >
    <Grid>
        <TextBlock Grid.Column="1" Text="{Binding Path=Country}" FontWeight="Bold" Margin="4" />
    </Grid>
</HierarchicalDataTemplate>
 
<HierarchicalDataTemplate DataType="{x:Type northwind:UnitsInStockGroup}" ItemsSource="{Binding Path=Products}">
    <TextBlock Text="{Binding Path=StockGroup}" FontWeight="Bold" />
</HierarchicalDataTemplate>
<TreeView Name="treeView2">
</TreeView>

4) Call the GetSupplierCountries_StockGroups_Products method in the partial Window class and set the ItemSource.

public partial class WindowTreeView : Window
{
  public WindowTreeView()
  {
    InitializeComponent();
 
    NorthwindData.DataProvider dataProvider = new NorthwindData.DataProvider();
 
    treeView2.ItemsSource = dataProvider.GetSupplierCountries_StockGroups_Products();
  }
}
5) Run the application.

 

 

Example 2B

2B : Create 2 value converter classes to improve the layout

  • LINQ to SQL : Grouping
  • WPF : 3-levels databinding with TreeView
  • WPF : Resources & templates
  • WPF : Value converter to show language-dependent descriptions
  • WPF : Value converter to get resource bitmap

1) The UnitsInStockGroup class has a boolean StockGroup property. This boolean value is the second level in our treeview. I would like to change this True/False value and display a language-dependent description. Value Converters are a great tool for formatting data in WMF.

Therefore let's create a UnitsInStockGroupValueConverter class which implements the IValueConverter interface. We need a one-way conversion so only implement the Convert method. Check the culture parameter to decide which language is being used.

public class UnitsInStockGroupValueConverter : IValueConverter
{
  public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
  {
    if ((bool)value)
    {
      if (culture.TwoLetterISOLanguageName == "nl")
        return "Grote stock";
      else
        return "Big stock";
    }
    else
    {
      if (culture.TwoLetterISOLanguageName == "nl")
        return "Kleine stock";
      else
        return "Small stock";
    }
  }
 
  public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
  {
    throw new NotSupportedException();
  }
}

2) It would be nice to show a bitmap of the flag of the supplier country in the main nodes. So I downloaded some flag icons (http://www.famfamfam.com/lab/icons/flags), renamed them and added the ones I needed to the Images folder in my project.

Now it is quite easy to create a new CountryFlagConverter class which returns a resource bitmap. The value parameter of the Convert method will contain the country name.

public class CountryFlagConverter : IValueConverter
{
  public object Convert(object value, Type targetType, object parameter,
    System.Globalization.CultureInfo culture)
  {
    if (value != null)
      return new BitmapImage(new Uri("Images/"+value.ToString()+".png", UriKind.Relative));
    else
      return null;
  }
 
  public object ConvertBack(object value, Type targetType, object parameter,
    System.Globalization.CultureInfo culture)
  {
    throw new NotSupportedException();
  }
}

3) Create an instance of each ValueConverter class in the XAML file.

<local:CountryFlagConverter x:Key="CountryFlagConverter"></local:CountryFlagConverter>
<local:UnitsInStockGroupValueConverter x:Key="UnitsInStockGroupValueConverter" />

4) Use the converters in the HierarchicalDataTemplates.

<HierarchicalDataTemplate DataType="{x:Type northwind:SupplierCountryStockGroup}" ItemsSource="{Binding Path=StockGroups}" >
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition></ColumnDefinition>
            <ColumnDefinition></ColumnDefinition>
        </Grid.ColumnDefinitions>
        <Image Grid.Column="0" Margin="4" Stretch="None" 
          Source="{Binding Path=Country, Converter={StaticResource CountryFlagConverter}}" />
        <TextBlock Grid.Column="1" Text="{Binding Path=Country}" FontWeight="Bold" Margin="4" />
    </Grid>
</HierarchicalDataTemplate>
 
<HierarchicalDataTemplate DataType="{x:Type northwind:UnitsInStockGroup}" ItemsSource="{Binding Path=Products}">
    <TextBlock Text="{Binding Path=StockGroup, Converter={StaticResource UnitsInStockGroupValueConverter}}" FontWeight="Bold" />
</HierarchicalDataTemplate>
5) Run the application. This will be the final result.



Example 3

3 : Show a reorganized treeview with countries and different items for USA & UK employees. Display the photo of the employee and use advanced styling techniques.

  • LINQ to SQL : Inheritance
  • LINQ to SQL : Grouping
  • WPF : 2-levels databinding with TreeView
  • WPF : Resources & templates
  • WPF : Value converter to get bitmap from database
  • WPF : Styles
  • WPF : Image reflection

1) Create 2 new derived classes UKEmployee and USAEmployee. See my LINQ to SQL - part 2 - Inheritance article for more information about inheritance with LINQ to SQL.

2) Create a custom EmployeeCountry class and add a GetCountries_Employees method in the DataProvider class.

namespace NorthwindData
{
 
  public class EmployeeCountry
  {
    public string Country { get; set; }
    public IEnumerable<Employee> Employees { get; set; }
  }
 
  public class DataProvider
  {
    NorthWindDataContext dc;
 
    public DataProvider()
    {
      dc = new NorthWindDataContext();
    }
 
    public IEnumerable<EmployeeCountry> GetCountries_Employees()
    {
      return from e in dc.Employees
             group e by e.Country into gr
             select new EmployeeCountry() { Country = gr.Key, Employees = gr };
    }
  }
}
3) Call the GetCountries_Employees method in the constructor of the Window.
public partial class WindowTreeView : Window
{
  public WindowTreeView()
  {
    InitializeComponent();
 
    NorthwindData.DataProvider dataProvider = new NorthwindData.DataProvider();
 
    treeView3.ItemsSource = dataProvider.GetCountries_Employees();
  }
}

4) The photos of the employees are stored in the database as an array of bytes (Photo field in table Employee). We need to implement an ImageBytesConverter class which will read the bytes into a memory stream and return a WPF BitmapImage.

public class ImageBytesConverter : IValueConverter
{
  public object Convert(object value, Type targetType, object parameter,
    System.Globalization.CultureInfo culture)
  {
    BitmapImage bitmap = new BitmapImage();
    if (value != null)
    {
      byte[] photo = (byte[])value;
      MemoryStream stream = new MemoryStream();
 
      // Work-around to make Northwind images work
      int offset = 78;
      stream.Write(photo, offset, photo.Length - offset);
 
      bitmap.BeginInit();
      bitmap.StreamSource = stream;
      bitmap.EndInit();
    }
    return bitmap; 
  }
 
  public object ConvertBack(object value, Type targetType, object parameter,
    System.Globalization.CultureInfo culture)
  {
    throw new NotSupportedException();
  }
}

5) Create an instance of the ImageBytesConverter class in the XAML file.

<local:ImageBytesConverter x:Key="ImageBytesConverter"></local:ImageBytesConverter>
6) Declare 2 styles : GroupBorderStyle which will be used in the Country (level 1) template and ShadowStyle which is a simple shadow effect.
<Style x:Key="GroupBorderStyle" TargetType="Border">
    <Setter Property="Control.BorderBrush" Value="Black" />
    <Setter Property="Control.BorderThickness" Value="1" />
    <Setter Property="Control.Margin" Value="8" />
    <Setter Property="Control.Padding" Value="5" />
    <Setter Property="Border.CornerRadius" Value="15" />
    <Setter Property="Control.Background">
        <Setter.Value>
            <LinearGradientBrush>
                <LinearGradientBrush.GradientStops>
                    <GradientStop Offset="0.00" Color="DarkGray" />
                    <GradientStop Offset="1.00" Color="LightGray" />
                </LinearGradientBrush.GradientStops>
            </LinearGradientBrush>
        </Setter.Value>
    </Setter>
    <Setter Property="Control.BitmapEffect">
        <Setter.Value>
            <DropShadowBitmapEffect Color="Black" Direction="315" ShadowDepth="5" Softness="0.25" Opacity="0.5"/>
        </Setter.Value>
    </Setter>
</Style>
 
<Style x:Key="ShadowStyle">
    <Setter Property="Control.BitmapEffect">
        <Setter.Value>
            <DropShadowBitmapEffect Color="Black" Direction="315" ShadowDepth="3" Softness="0.1" Opacity="0.5"/>
        </Setter.Value>
    </Setter>
</Style>
7) Create a HierarchicalDataTemplate for level 1. This template will be linked to an EmployeeCountry object. The subnodes are Employees.
<HierarchicalDataTemplate DataType="{x:Type northwind:EmployeeCountry}" ItemsSource="{Binding Path=Employees}">
    <Border Style="{StaticResource GroupBorderStyle}" Width="250">
        <Grid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition>
                </ColumnDefinition>
                <ColumnDefinition>
                </ColumnDefinition>
            </Grid.ColumnDefinitions>
            <Image Grid.Column="0" Width="16" Margin="4" Style="{StaticResource ShadowStyle}" 
              Source="{Binding Path=Country, Converter={StaticResource CountryFlagConverter}}" >
            </Image>
            <TextBlock Grid.Column="1" TextAlignment="Left" Style="{StaticResource ShadowStyle}" 
              Text="{Binding Path=Country}" Foreground="White" FontSize="14" FontWeight="Bold" Margin="4" >
            </TextBlock>
        </Grid>
    </Border>
</HierarchicalDataTemplate>
8) Create a template for the UKEmployee class. The ImageBytesConverter is used to display to photo of the employee.
<HierarchicalDataTemplate DataType="{x:Type northwind:UKEmployee}">
    <Border Style="{StaticResource GroupBorderStyle}" Background="White" Width="230">
        <Grid Margin="6">
            <Grid.ColumnDefinitions>
                <ColumnDefinition>
                    <ColumnDefinition.Width>90</ColumnDefinition.Width>
                </ColumnDefinition>
                <ColumnDefinition></ColumnDefinition>
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
                <RowDefinition>
                    <RowDefinition.Height>20</RowDefinition.Height>
                </RowDefinition>
                <RowDefinition>
                    <RowDefinition.Height>20</RowDefinition.Height>
                </RowDefinition>
                <RowDefinition>
                    <RowDefinition.Height>20</RowDefinition.Height>
                </RowDefinition>
                <RowDefinition>
                    <RowDefinition.Height>20</RowDefinition.Height>
                </RowDefinition>
            </Grid.RowDefinitions>
 
            <StackPanel Grid.Row="0" Grid.Column="0" Grid.RowSpan="4" Height="80" Width="70">
                <Border x:Name="USAEmployeePhoto" BorderBrush="Gray" BorderThickness="2" 
                   HorizontalAlignment="Center" VerticalAlignment="Center">
                    <Border BorderBrush="White" BorderThickness="2">
                        <Image  Stretch="UniformToFill"
                          HorizontalAlignment="Left" 
                          Source="{Binding Path=Photo, Converter={StaticResource ImageBytesConverter}}">
                        </Image>
                    </Border>
                </Border>
             </StackPanel>
 
            <WrapPanel Grid.Row="0" Grid.Column="1">
                <TextBlock Text="{Binding Path=FirstName}" Padding="2" Foreground="Gray" FontWeight="Bold"/>
                <TextBlock Text="{Binding Path=LastName}" Padding="2" Foreground="Gray" FontWeight="Bold" />
            </WrapPanel>
            <TextBlock Grid.Row="1" Grid.Column="1" Text="{Binding Path=Address}" Padding="2" />
            <WrapPanel Grid.Row="2" Grid.Column="1">
                <TextBlock Text="{Binding Path=City}" Padding="2" />
                <TextBlock Text="{Binding Path=PostalCode}" Padding="2" />
            </WrapPanel>
            <WrapPanel Grid.Row="3" Grid.Column="1">
                <TextBlock Text="{Binding Path=HomePhone}" Padding="2" />
            </WrapPanel>
        </Grid>
    </Border>
</HierarchicalDataTemplate>
9) Create an alternative template for the USAEmployee class. Use a VisualBrush to create a mirrored reflection of the photo.
<HierarchicalDataTemplate DataType="{x:Type northwind:USAEmployee}">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition> 
                <ColumnDefinition.Width>70</ColumnDefinition.Width>
            </ColumnDefinition>
            <ColumnDefinition></ColumnDefinition>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition>
                <RowDefinition.Height>25</RowDefinition.Height>
            </RowDefinition>
            <RowDefinition>
                <RowDefinition.Height>25</RowDefinition.Height>
            </RowDefinition>
            <RowDefinition>
                <RowDefinition.Height>50</RowDefinition.Height>
            </RowDefinition>
        </Grid.RowDefinitions>
 
        <StackPanel Grid.Row="0" Grid.Column="0" Grid.RowSpan="4" Height="100" Width="50">
            <Border x:Name="USAEmployeePhoto" BorderBrush="Gray" BorderThickness="2"  
               HorizontalAlignment="Center" VerticalAlignment="Center" Height="60">
                <Border BorderBrush="White" BorderThickness="2">
                <Image  Stretch="UniformToFill"
                  HorizontalAlignment="Left" 
                  Source="{Binding Path=Photo, Converter={StaticResource ImageBytesConverter}}">
                </Image>
            </Border>
            </Border>
            <Border Height="40">
                <Border.Background>
                    <VisualBrush Visual="{Binding ElementName=USAEmployeePhoto}">
                        <VisualBrush.Transform>
                            <ScaleTransform ScaleX="1" ScaleY="-1" CenterX="10" CenterY="20"></ScaleTransform>
                        </VisualBrush.Transform>
                    </VisualBrush>
                </Border.Background>
                <Border.OpacityMask>
                    <LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
                        <GradientStop Offset="0.0" Color="Black"></GradientStop>
                        <GradientStop Offset="0.8" Color="Transparent"></GradientStop>
                    </LinearGradientBrush>
                </Border.OpacityMask>
            </Border>
        </StackPanel>
 
        <WrapPanel Grid.Row="0" Grid.Column="1">
            <TextBlock Text="{Binding Path=FirstName}" Padding="2" FontWeight="Bold"/>
            <TextBlock Text="{Binding Path=LastName}" Padding="2" FontWeight="Bold" />
        </WrapPanel>
        <TextBlock Grid.Row="1" Grid.Column="1" Text="{Binding Path=Address}" Padding="2" />
        <WrapPanel Grid.Row="2" Grid.Column="1">
            <Image Width="16" Margin="4" 
              Source="{Binding Path=Country, Converter={StaticResource CountryFlagConverter}}" />
            <TextBlock Text="{Binding Path=City}" Padding="2" />
            <TextBlock Text="{Binding Path=Region}" Padding="2" FontWeight="Bold" />
            <TextBlock Text="{Binding Path=PostalCode}" Padding="2" />
        </WrapPanel>
    </Grid>
</HierarchicalDataTemplate>

10) Finally, change the ItemsPanel template. We would like to arrange the root items (EmployeeCountry) in a horizontal row. Therefore add a StackPanel with a Horizontal Orientation.

<TreeView Name="treeView3">
    <TreeView.ItemsPanel>
        <ItemsPanelTemplate>
            <StackPanel IsItemsHost="True" Orientation="Horizontal" />
        </ItemsPanelTemplate>
    </TreeView.ItemsPanel>
</TreeView>

11) The result demonstrates the power of WPF/Silverlight and LINQ. With a minimum of efforts we created a reorganized treeview with photos of the employees. The treeview uses different layouts for UK and USA employee items which are retrieved from a SQL Server database.