Creating a simple Pivot table using LINQ and Telerik RadTreeView for Silverlight

This article is compatible with the latest version of Silverlight.


What is a Pivot table/grid? According to Wikipedia it is a data summarization tool found in spreadsheet applications. Still when I was a child I learned that people understand the best when they see an example.

Consider you have a table that contains the nutrition of given food, say a pizza:

GroupNameQuantity
CarbohydratesTotal carbohydrates27.3
CarbohydratesTotal disaccharides5.7
CarbohydratesTotal polysaccharides21.6
MineralsCalcium147
MineralsPhosphorus150
MineralsPotassium201
MineralsCopper0.13
MineralsMagnesium19
MineralsSodium582
MineralsSelenium4
MineralsTotal iron0.7
MineralsZinc1.07
VitaminsBeta-carotene173.8
VitaminsNicotinic1.5
VitaminsTotal vitamin B60.127
VitaminsTotal vitamin D0.3
VitaminsTotal vitamin E2.1
VitaminsVitamin B10.1
VitaminsVitamin B120.59
VitaminsVitamin B20.16
VitaminsVitamin C10

In the data above you see that every nutrition is contained in a specific Group - 3 groups and 21 nutrition in total. To display the nutrition in a more meaningful way in most cases you need to group the nutrition (rows in the general case) by their Group attribute and display them in columns instead of in rows. Now we are closer to what we call a Pivot table - group and turn rows into columns.

So the above table displayed in a Pivot would look like that:

Carbohydrates Minerals Vitamins 
Total carbohydrates27.3Calcium147Beta-carotene173.8
Total disaccharides5.7Phosphorus150Nicotinic1.5
Total polysaccharides21.6Potassium201Total vitamin B60.127
  Copper0.13Total vitamin D0.3
  Magnesium19Total vitamin E2.1
  Sodium582Vitamin B10.1
  Selenium4Vitamin B120.59
  Total iron0.7Vitamin B20.16
  Zinc1.07Vitamin C10

I searched for a 3rd party control that can help me achieve this goal out of the box, but I couldn't find one. Looking for a way to do it I finally made it work with the help of LINQ - for grouping and the Telerik RadTreeView for Silverlight - for displaying.

Loading the data with LINQ

First, consider we have the initial table of nutrition exported to XML. The output look like that:

    
    
<?xml version="1.0" encoding="utf-8" ?>
<Nutritions>
    <Nutrition Group="Carbohydrates" Name="Total carbohydrates" Quantity="27.3"></Nutrition>
    <Nutrition Group="Carbohydrates" Name="Total disaccharides" Quantity="5.7"></Nutrition>
    <Nutrition Group="Carbohydrates" Name="Total polysaccharides" Quantity="21.6"></Nutrition>
    <Nutrition Group="Minerals" Name="Calcium" Quantity="147"></Nutrition>
    <Nutrition Group="Minerals" Name="Phosphorus " Quantity="150"></Nutrition>

Create a business object Nutrition that will be used later when loading the XML with LINQ.

    
    
public class Nutrition
{
    public string Group { get; set; }
    public string Name { get; set; }
    public string Quantity { get; set; }
}

and NutritionGroup:

 public class NutritionGroup
 {
    public string NutritionGroupHeader { getset; }
    public Collection<Nutrition> Nutritions { getset; }
 }

Now, it's time to load the XML document with LINQ. Martin Mihaylov published a great article on using LINQ to XML in Silverlight so if you are not familiar you'd better go read it first.

    
    
List data = ( from nutrition in nutritionsDoc.Descendants( "Nutrition" )
            select new Nutrition
            {
                Group = nutrition.Attribute( "Group" ).Value,
                Name = nutrition.Attribute( "Name" ).Value,
                Quantity = nutrition.Attribute( "Quantity" ).Value
            } ).ToList();

Grouping the data with LINQ and building the tree

Ok, this is the core of this article. The ability to group is a great feature in LINQ. It really does simplify the code to minimum.

We need to group by the Group attribute.

 IEnumerable<string, Nutrition>> query = data.GroupBy( nutrition => nutrition.Group );

Now, each nutrition group should be a root node in the tree and each nutrition in this group should be added to the nutritions in the corresponding root. When the ItemSource property is set to the collection of nutrition groups, the RadTreeView will be populated with the corresponding data:

    
    
Collection<NutritionGroup> nutritions = new Collection<NutritionGroup>();
 
foreach ( IGrouping<string, Nutrition> nutritionGroup in query )
{
    NutritionGroup group = new NutritionGroup()
    {
        NutritionGroupHeader = nutritionGroup.Key,
    };
    group.Nutritions = new Collection<Nutrition>();
 
    foreach ( Nutrition nutrition in nutritionGroup )
    {
        group.Nutritions.Add( nutrition );
    }
 
    nutritions.Add( group );
}
 
nutritionTree.ItemsSource = nutritions;

Ok, we are almost over. Let's take a look at the NutritionGroupTemplate and NutritionTemplate that we will use. First, we will need the following schema:

Having that, follows both the templates:

NutritionGroupTemplate

     
     
<telerik:HierarchicalDataTemplate x:Key="NutritionGroupTemplate"
          ItemsSource="{Binding Nutritions}" ItemTemplate="{StaticResource NutritionTemplate}">
    <Border BorderThickness="1" BorderBrush="#ececec" CornerRadius="4">
        <Border BorderThickness="1" BorderBrush="White" Padding="1" CornerRadius="4">
            <Border.Background>
                <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                    <GradientStop Color="#f8f8f8" />
                    <GradientStop Color="#ececec" Offset="1" />
                </LinearGradientBrush>
            </Border.Background>
            <TextBlock Text="{Binding NutritionGroupHeader}" FontWeight="Bold" />
        </Border>
    </Border>
</telerik:HierarchicalDataTemplate>

NutritionTemplate

   
   
<telerik:HierarchicalDataTemplate x:Key="NutritionTemplate">
    <StackPanel Orientation="Horizontal">
        <TextBlock Text="{Binding Name}" Width="150"></TextBlock>
        <TextBlock Text="{Binding Quantity}" FontWeight="Bold"></TextBlock>
    </StackPanel>
</telerik:HierarchicalDataTemplate>

The default orientation of the nodes in the RadTreeView, as expected, is vertical. However in our case it makes more sense to arrange the root nodes on the horizontal and only the child elements to the vertical:

     
     
<Style TargetType="telerik:RadTreeViewItem" x:Key="TreeViewItemStyle">
    <Setter Property="IsExpanded" Value="True"></Setter>
    <Setter Property="ItemsPanel">
        <Setter.Value>
            <ItemsPanelTemplate>
                <StackPanel HorizontalAlignment="Center"
                            Margin="4,6" Orientation="Vertical" />
            </ItemsPanelTemplate>
        </Setter.Value>
    </Setter>
</Style>

And the RadTreeView element:

<telerik:RadTreeView x:Name="nutritionTree"
                     ItemTemplate="{StaticResource NutritionGroupTemplate}"
                     ItemContainerStyle="{StaticResource TreeViewItemStyle}">
    <telerik:RadTreeView.ItemsPanel>
        <ItemsPanelTemplate>
            <StackPanel VerticalAlignment="Top" Orientation="Horizontal" />
        </ItemsPanelTemplate>
    </telerik:RadTreeView.ItemsPanel>
</telerik:RadTreeView>
http://www.silverlightshow.net/items/Creating-a-simple-Pivot-table-using-LINQ-and-RadTreeView-for-Silverlight.aspx
文章转载from:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值