Blogs

The Progress Bar - DevExpress XPF Blog

July 2011 - Posts

  • WPF and Silverlight Data Grid – MVVM Enhancements

         

    For typical WPF/Silverlight business application scenarios you can quickly create and setup the DevExpress Grid in XAML:

    1. <dxg:GridControl ItemsSource="{Binding Source}">
    2.     <dxg:GridControl.Columns>
    3.         <dxg:GridColumn FieldName="FirstName"/>
    4.         <dxg:GridColumn FieldName="LastName"/>
    5.     </dxg:GridControl.Columns>
    6.     <dxg:GridControl.TotalSummary>
    7.         <dxg:GridSummaryItem FieldName="FirstName" SummaryType="Count"/>
    8.     </dxg:GridControl.TotalSummary>
    9. </dxg:GridControl>

    When engineering Silverlight/WPF applications using the Model-View-ViewModel (MVVM) architectural pattern, you may be required to put the code that describes columns and summaries in Model or ViewModel.

    In previous versions of the DXGrid (prior to v2011 vol 1), columns could have been described via DataAnnotations attributes. In this instance, the grid automatically generated columns for all fields in a data source.   The only way to customize column settings was to handle the grid’s ColumnsPopulated event in ‘code-behind’.

    With the release of DXperience v2011 vol 1, DevExpress WPF/Silverlight Grid Controls support column and summary binding capabilities. The grid can be bound to ViewModel properties that represent collections of objects with column and summary settings, thus minimizing the need for ‘code-behind’ and placing column and summary definition logic in the ViewModel.

    Three steps to populate columns and create summary items from the ViewModel:

    • Describe columns and/or summaries in the ViewModel.
    • Create columns and summary items based on ViewModel settings.
    • Setup  Grid Control bindings.

    View Model Implementation

    Let’s consider an Employee business object that contains employee information.

    1. public class Employee
    2. {
    3.     public string FirstName { get; set; }
    4.     public string LastName { get; set; }
    5.     public string JobTitle { get; set; }
    6.     public string City { get; set; }
    7.     public DateTime BirthDate { get; set; }
    8. }

    In a Grid Control, data fields (public properties) are represented as columns. To describe a grid column in the ViewModel, create a class whose properties correspond to column settings. Let’s create two classes:

    • Column - describes a grid column used by default. This class provides properties that correspond to settings common to all grid columns types.
    • ComboBoxColumn - corresponds to a grid column with a ComboBoxEdit in-place editor. This class provides the Source property that contains a list of  combo box items. We’ll use this class to represent an employee’s City.
    1. public class Column
    2. {
    3.     public string FieldName { get; set; }
    4.     public SettingsType Settings { get; set; }
    5. }
    6.  
    7. public class ComboColumn : Column
    8. {
    9.     public IList Source { get; set; }
    10. }
    11.  
    12. // Enumerates possible types of in-place editors used to edit cell values.
    13. public enum SettingsType { Default, Combo }

    Next, we’ll create a collection of Column objects in the ViewModel:

    1. public class ViewModel {
    2.     ...
    3.     public List<string> Cities { get; private set; }
    4.         
    5.     public ObservableCollection<Column> Columns { get; private set; }
    6.  
    7.     public ViewModel() {
    8.         ...
    9.         List<string> _cities = new List<string>();
    10.         foreach (Employee employee in Source) {
    11.             if (!_cities.Contains(employee.City))
    12.                 _cities.Add(employee.City);
    13.         }
    14.         Cities = _cities;
    15.         Columns = new ObservableCollection<Column>() {
    16.             new Column() { FieldName = "FirstName", Settings = SettingsType.Default },
    17.             new Column() { FieldName = "LastName", Settings = SettingsType.Default },
    18.             new Column() { FieldName = "BirthDate", Settings = SettingsType.Default },
    19.             new ComboColumn() { FieldName = "City", Settings = SettingsType.Combo, Source = Cities }
    20.         };
    21.     }
    22. }

    To describe data summaries, we’ll create a corresponding class with summary settings:

    1. public class Summary
    2. {
    3.     public SummaryItemType Type { get; set; }
    4.     public string FieldName { get; set; }
    5. }

    In the ViewModel, we’ll create GroupSumary and TotalSummary collections:

    1. public ObservableCollection<Summary> TotalSummary { get; private set; }
    2. public ObservableCollection<Summary> GroupSummary { get; private set; }

    Note that we use ObservableCollection to represent the Columns, GroupSummary and TotalSummary collections. These collections should implement INotifyCollectionChanged so that changes made within the ViewModel are automatically reflected by the Grid Control.

    Column Templates and Template Selector

    WPF and Silverlight data templating model is a perfect solution for representing grid columns based on settings specified in the ViewModel.

    Because of this flexibility, we’ll create multiple column templates - one template for each column type. By using a single template, we can create an unlimited number of columns in an unlimited number of grids.

    1. <Window.Resources>
    2.     <view:ColumnTemplateSelector x:Key="ColumnTemplateSelector"/>
    3.     <DataTemplate x:Key="DefaultColumnTemplate">
    4.         <ContentControl>
    5.             <dxg:GridColumn FieldName="{Binding FieldName}"/>
    6.         </ContentControl>
    7.     </DataTemplate>
    8.     <DataTemplate x:Key="ComboColumnTemplate">
    9.         <ContentControl>
    10.             <dxg:GridColumn FieldName="{Binding FieldName}">
    11.                 <dxg:GridColumn.EditSettings>
    12.                     <dxe:ComboBoxEditSettings ItemsSource="{Binding Source}"/>
    13.                 </dxg:GridColumn.EditSettings>
    14.             </dxg:GridColumn>
    15.         </ContentControl>
    16.     </DataTemplate>
    17. </Window.Resources>

    If all grid columns can be described using a single template, assign this template to the grid’s ColumnGeneratorTemplate property; Otherwise, create a Data Template Selector.

    Note: No need to worry that Silverlight does not ship with a DataTemplateSeletor. This class is included in our DevExpress.Xpf.Core library and is widely used in our cross-platform controls.

    1. using System.Windows;
    2. using System.Windows.Controls;
    3. using Model;
    4.  
    5. namespace View {
    6.     public class ColumnTemplateSelector : DataTemplateSelector {
    7.         public override DataTemplate SelectTemplate(object item, DependencyObject container) {
    8.             Column column = (Column)item;
    9.             return (DataTemplate)((Control)container).FindResource(column.Settings + "ColumnTemplate");
    10.         }
    11.     }
    12. }

    You can create a style to specify settings common to all columns generated using different templates. In WPF and Silverlight 5, you can specify bindings to ViewModel properties within a style (see FieldName below):

    This style should be assigned to the grid’s ColumnGeneratorStyle property.

    1. <Style x:Key="ColumnStyle" TargetType="dxg:GridColumn">
    2.     <Setter Property="FilterPopupMode" Value="CheckedList"/>
    3.     <Setter Property="FieldName" Value="{Binding FieldName}"/>
    4. </Style>

    Summary Template

    Summary items are also generated from templates. A template that describes total summaries should be assigned to the grid’s TotalSummaryGeneratorTemplate property. A group summary template should be assigned to the GroupSummaryGeneratorTemplate property.

    For purposes of this blog, it will be a single template for group and total summaries.

    1. <DataTemplate x:Key="SummaryTemplate">
    2.     <ContentControl>
    3.         <dxg:GridSummaryItem FieldName="{Binding Path=(dx:DependencyObjectExtensions.DataContext).FieldName, RelativeSource={RelativeSource Self}}"
    4.                     SummaryType="{Binding Path=(dx:DependencyObjectExtensions.DataContext).Type, RelativeSource={RelativeSource Self}}"/>
    5.     </ContentControl>
    6. </DataTemplate>

    Bindings Setup

    For the final step, we’ll bind the grid to the Columns, TotalSummary and GroupSummary properties in the ViewModel, specify the column template selector, total and group summary templates.

    1. <dxg:GridControl Name="grid"
    2.             ItemsSource="{Binding Source}"
    3.             ColumnsSource="{Binding Columns}"
    4.             ColumnGeneratorTemplateSelector="{StaticResource ColumnTemplateSelector}"
    5.             TotalSummarySource="{Binding TotalSummary}"
    6.             TotalSummaryGeneratorTemplate="{StaticResource SummaryTemplate}"
    7.             GroupSummarySource="{Binding GroupSummary}"
    8.             GroupSummaryGeneratorTemplate="{StaticResource SummaryTemplate}">
    9.     <dxg:GridControl.View>
    10.         <dxg:TableView Name="tableView1"
    11.                 AutoWidth="True"
    12.                 NavigationStyle="Cell"
    13.                 ShowTotalSummary="True"
    14.                 IsTotalSummaryMenuEnabled="False"/>
    15.     </dxg:GridControl.View>
    16. </dxg:GridControl>

    To see the full source code for this post, download the following example: http://www.devexpress.com/Support/Center/e/E3358.aspx.

    Keep tuned to this blog for all the MVVM enhancements shipping with DevExpress Grid Controls for WPF and Silverlight.

  • Silverlight and WPF Video Training Roundup

         

    During the last month, we’ve put together a number of videos demonstrating the use of several products and features that became available with the latest 2011.1 release of DXperience. Here is a roundup of the tutorial videos to help you get started with our most recent improvements and additions:

More from DevExpress
Live Chat
Have a pre-sales question?
Need assistance with your evaluation?
We are here to help.
Chat is one of the many ways you can contact members of the DevExpress Team. We are available Monday-Friday between 7:30am and 4:30pm Pacific Time.
If you need additional product information, require pre-sales assistance, or want help with your order, write to us at info@devexpress.com or call us at
+1 (818) 844-3383.