Binding to a Collection of Fields

  • 5 minutes to read

When engineering a WPF application using the Model View ViewModel (MVVM) architectural pattern, you may be required to describe fields in a Model or ViewModel. The Pivot Grid can be bound to a collection of objects containing field settings, described in a Model or ViewModel, thus minimizing the need for 'code-behind'.

View Model Implementation

NOTE

In this tutorial PivotGrid is bound to the Sales Person view of the Northwind database. You can find it at C:\Users\Public\Documents\DevExpress Demos 20.1\Components\Data\nwind.mdb.

Create a view model. In this tutorial the view model includes the following classes:

  • ViewModel - the Sales Person view model.
  • Field - describes the Pivot Grid fields. This class provides properties that correspond to settings common to all Pivot Grid fields.
  • FieldTemplateSelector - describes the Pivot Grid template selector. This class allows you to choose the required template based on the specified condition.
using System.Windows;
using System.Windows.Controls;
using System.Collections.ObjectModel;
using DevExpress.Xpf.PivotGrid;

namespace Model{
  public class ViewModel {

    // The collection of Pivot Grid fields.
    public ObservableCollection<Field> Fields { get; private set; }

    // The view model that contains the field settings.
    public ViewModel() {
      Fields = new ObservableCollection<Field>() {
        new Field() { DataFieldName="CategoryName", AreaIndex=0, FieldArea = FieldArea.RowArea, UniqueName="fieldCategory" },
        new Field() { DataFieldName="Country", AreaIndex=0, FieldArea = FieldArea.ColumnArea, UniqueName="fieldCountry" },
        new Field() { DataFieldName="Sales Person", AreaIndex=1, FieldArea = FieldArea.ColumnArea, UniqueName="fieldSalesPerson" },
        new Field() { DataFieldName="OrderDate", AreaIndex=0, FieldArea = FieldArea.FilterArea, UniqueName="fieldOrderYear",
            Interval = FieldGroupInterval.DateYear, FieldCaption = "Year", GroupName="groupYearMonth", GroupIndex=0 },
        new Field() { DataFieldName="Extended Price", AreaIndex=0, FieldArea = FieldArea.DataArea, UniqueName="fieldPrice" },
      };            
    }
  }

  public class Field {
    public string DataFieldName { get; set; }
    public string UniqueName { get; set; }
    public string FieldCaption { get; set; }
    public FieldArea FieldArea { get; set; }
    public int AreaIndex { get; set; }
    public FieldGroupInterval Interval { get; set; }
    public string GroupName { get; set; }
    public int GroupIndex { get; set; }
  }

  public class FieldTemplateSelector : DataTemplateSelector {
    public override DataTemplate SelectTemplate(object item, DependencyObject container) {
      Field field = (Field)item;
        if(field.Interval == FieldGroupInterval.DateYear) {
          return (DataTemplate)((Control)container).FindResource("IntervalFieldTemplate");
        }
      return (DataTemplate)((Control)container).FindResource("DefaultFieldTemplate");
    }
  }
}
NOTE

If the Fields collection might be changed after it has been assigned to the Pivot Grid control, it should implement the INotifyCollectionChanged interface, so that changes made within a View Model are automatically reflected by the Pivot Grid.

Field Templates and Selector

The Pivot Grid Control generates fields based on field templates. Using a template, you can create an unlimited number of fields in an unlimited number of Pivot Grid controls. In this example, there are two field templates: DefaultFieldTemplate and ComboColumnTemplate.

To avoid performance issues when binding to field properties, use the dxci:DependencyObjectExtensions.DataContext attached property. See the example below.

<!-- -->
xmlns:dxci="http://schemas.devexpress.com/winfx/2008/xaml/core/internal"
<!-- -->
    <DataTemplate x:Key="DefaultFieldTemplate">
      <ContentControl>
        <dxpg:PivotGridField FieldName="{Binding Path=(dxci:DependencyObjectExtensions.DataContext).FieldName, RelativeSource={RelativeSource Self}}"
                   Area="{Binding Path=(dxci:DependencyObjectExtensions.DataContext).FieldArea, RelativeSource={RelativeSource Self}}"
                   AreaIndex="{Binding Path=(dxci:DependencyObjectExtensions.DataContext).AreaIndex, RelativeSource={RelativeSource Self}}"
                   Caption="{Binding Path=(dxci:DependencyObjectExtensions.DataContext).FieldCaption, RelativeSource={RelativeSource Self}}"
                   dx:XamlHelper.Name="{Binding Path=(dxci:DependencyObjectExtensions.DataContext).UniqueName, RelativeSource={RelativeSource Self}}"
                   >
        </dxpg:PivotGridField>
      </ContentControl>
    </DataTemplate>

To choose the required template based on the specified condition, use the Template Selector. In this example, the template selector is represented by the FieldTemplateSelector class:

<Window x:Class="WpfPivotTestExample.MainWindow"
        x:Name="Form1" Title="MainWindow" Height="362" Width="627"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:WpfPivotTestExample"
        xmlns:dxpg="http://schemas.devexpress.com/winfx/2008/xaml/pivotgrid" 
        xmlns:dx="http://schemas.devexpress.com/winfx/2008/xaml/core" 
        xmlns:dxci="http://schemas.devexpress.com/winfx/2008/xaml/core/internal"
        xmlns:nwindDataSetTableAdapters="clr-namespace:WpfPivotTestExample.nwindDataSetTableAdapters" 
        xmlns:model="clr-namespace:Model" 
        xmlns:view="clr-namespace:View">
    <Window.DataContext>
        <local:ViewModel />
    </Window.DataContext>
    <Window.Resources>
        <local:FieldTemplateSelector x:Key="FieldTemplateSelector" />
        <dx:TypedSimpleSource x:Key="TypedSimpleSource" AdapterType="{x:Type nwindDataSetTableAdapters:SalesPersonTableAdapter}" 
                              ContextType="{x:Type local:nwindDataSet}" Path="SalesPerson" />
        <DataTemplate x:Key="DefaultFieldTemplate">
            <ContentControl>
                <dxpg:PivotGridField FieldName="{Binding Path=(dxci:DependencyObjectExtensions.DataContext).FieldName, RelativeSource={RelativeSource Self}}"
                                     Area="{Binding Path=(dxci:DependencyObjectExtensions.DataContext).FieldArea, RelativeSource={RelativeSource Self}}"
                                     AreaIndex="{Binding Path=(dxci:DependencyObjectExtensions.DataContext).AreaIndex, RelativeSource={RelativeSource Self}}"
                                     Caption="{Binding Path=(dxci:DependencyObjectExtensions.DataContext).FieldCaption, RelativeSource={RelativeSource Self}}"
                                     dx:XamlHelper.Name="{Binding Path=(dxci:DependencyObjectExtensions.DataContext).UniqueName, RelativeSource={RelativeSource Self}}"
                                     >
                </dxpg:PivotGridField>
            </ContentControl>
        </DataTemplate>
        <DataTemplate x:Key="IntervalFieldTemplate">
            <ContentControl>
                <dxpg:PivotGridField FieldName="{Binding Path=(dxci:DependencyObjectExtensions.DataContext).FieldName, RelativeSource={RelativeSource Self}}"
                                     Area="{Binding Path=(dxci:DependencyObjectExtensions.DataContext).FieldArea, RelativeSource={RelativeSource Self}}"
                                     AreaIndex="{Binding Path=(dxci:DependencyObjectExtensions.DataContext).AreaIndex, RelativeSource={RelativeSource Self}}"
                                     Caption="{Binding Path=(dxci:DependencyObjectExtensions.DataContext).FieldCaption, RelativeSource={RelativeSource Self}}"
                                     dx:XamlHelper.Name="{Binding Path=(dxci:DependencyObjectExtensions.DataContext).UniqueName, RelativeSource={RelativeSource Self}}"
                                     GroupInterval="{Binding Path=(dxci:DependencyObjectExtensions.DataContext).Interval, RelativeSource={RelativeSource Self}}"
                                     HeaderImage="{dx:DXImage Image=Calendar_16x16.png}">                                     
                </dxpg:PivotGridField>
            </ContentControl>
        </DataTemplate>
    </Window.Resources>
    <Grid>
        <!-- -->
    </Grid>
</Window>

If all Pivot Grid fields can be described using a single template, you have no need to create a field template selector. Instead, assign this template to the Pivot Grid's PivotGridControl.FieldGeneratorTemplate property.

You can create a style to specify settings common to all fields generated using different templates. You can specify bindings to ViewModel properties within a style. This style should be assigned to the PivotGridControl.FieldGeneratorStyle property.

Customizing the WPF DXPivotGrid

Finally, specify the Pivot Grid's PivotGridControl.DataSource, PivotGridControl.FieldsSource and PivotGridControl.FieldGeneratorTemplateSelector.

Member Description
PivotGridControl.DataSource Specifies the Pivot Grid's data source.
PivotGridControl.FieldsSource Specifies the source from which the Pivot Grid generates fields.
PivotGridControl.FieldGeneratorTemplateSelector Specifies the field template selector, which returns a template for each field based on the specified condition.

The following code shows how to configure the Pivot Grid control using the ViewModel.

<Grid>
        <dxpg:PivotGridControl x:Name="pivotGridControl1" 
                               DataSource="{Binding Data, Source={StaticResource TypedSimpleSource}}"
                               FieldsSource="{Binding Fields }" 
                               FieldGeneratorTemplateSelector="{StaticResource FieldTemplateSelector}"                               
        </dxpg:PivotGridControl>
    </Grid>

The image below shows the result. As you can see, the OrderDate field has a Calendar icon in its field header.

pivot-field-template

Example

How to: Bind the Pivot Grid to Fields and Groups specified in ViewModel

See Also