wpf-10121-controls-and-libraries-data-grid-mvvm-enhancements-examples-binding-to-a-collection-of-columns.md
This topic defines columns in a View Model and display them in the GridControl.
View Example: How to Bind the GridControl to a Collection of Columns Specified in a ViewModel
This scenario displays information from an employee data model in the GridControl.
Create a class that describes a grid column:
Specify a collection of columns in the ViewModel:
The GridControl generates columns based on column templates. Add a default column template.
Assign the column collection to the DataControlBase.ColumnsSource property. Set the DataControlBase.ColumnGeneratorTemplate property to a template that generates columns:
If you want to specify extra column types (for example, to display a list of states in a column’s combo box), follow the steps below:
Add an enumeration that lists column types:
Create a class that describes a lookup column:
Add a lookup column to the ViewModel’s Columns collection and specify the lookup’s data source:
Create a template that generates lookup columns:
Your application now contains the DefaultColumnTemplate and LookupColumnTemplate. To choose a template based on the column type, create a template selector and assign it to the DataControlBase.ColumnGeneratorTemplateSelector property:
using System.Windows;
using System.Windows.Controls;
namespace ColumnsSample {
public class ColumnTemplateSelector : DataTemplateSelector {
public DataTemplate DefaultColumnTemplate { get; set; }
public DataTemplate LookupColumnTemplate { get; set; }
public override DataTemplate SelectTemplate(object item, DependencyObject container) {
Column column = item as Column;
if(column == null) return null;
switch(column.Settings) {
case SettingsType.Default:
return DefaultColumnTemplate;
case SettingsType.Lookup:
return LookupColumnTemplate;
}
return null;
}
}
}
Imports System.Windows
Imports System.Windows.Controls
Namespace ColumnsSample
Public Class ColumnTemplateSelector
Inherits DataTemplateSelector
Public Property DefaultColumnTemplate() As DataTemplate
Public Property LookupColumnTemplate() As DataTemplate
Public Overrides Function SelectTemplate(ByVal item As Object, ByVal container As DependencyObject) As DataTemplate
Dim column As Column = TryCast(item, Column)
If column Is Nothing Then
Return Nothing
End If
Select Case column.Settings
Case SettingsType.Default
Return DefaultColumnTemplate
Case SettingsType.Lookup
Return LookupColumnTemplate
End Select
Return Nothing
End Function
End Class
End Namespace
<Window ...
xmlns:dxg="http://schemas.devexpress.com/winfx/2008/xaml/grid">
<Window.Resources>
<!-- ... -->
<local:ColumnTemplateSelector x:Key="ColumnTemplateSelector"
DefaultColumnTemplate ="{StaticResource DefaultColumnTemplate}"
LookupColumnTemplate ="{StaticResource LookupColumnTemplate}"/>
</Window.Resources>
<dxg:GridControl ItemsSource="{Binding Source}"
ColumnsSource="{Binding Columns}"
ColumnGeneratorTemplateSelector="{StaticResource ColumnTemplateSelector}"/>
</Window>
You can use the DataControlBase.ColumnGeneratorStyle property to create a style that specifies column settings. The GridControl applies these settings to all columns generated from templates:
<Window ...
xmlns:dxg="http://schemas.devexpress.com/winfx/2008/xaml/grid">
<dxg:GridControl.ColumnGeneratorStyle>
<Style TargetType="dxg:GridColumn">
<Setter Property="FilterPopupMode" Value="CheckedList" />
<Setter Property="Width" Value="Auto" />
<Setter Property="ReadOnly" Value="True" />
<Setter Property="HorizontalHeaderContentAlignment" Value="Center" />
<Setter Property="HeaderStyle">
<Setter.Value>
<Style TargetType="dxg:BaseGridHeader">
<Setter Property="FontWeight" Value="SemiBold" />
</Style>
</Setter.Value>
</Setter>
</Style>
</dxg:GridControl.ColumnGeneratorStyle>
</Window>
You can also use multiple data annotation attributes to define validation rules and format options:
public class Employee {
[ReadOnly(true)]
public string FirstName { get; set; }
[MinLength(3)]
public string LastName { get; set; }
public string StateProvinceName { get; set; }
}
Public Class Employee
<[ReadOnly](True)>
Public Property FirstName As String
<MinLength(3)>
Public Property LastName As String
Public Property StateProvinceName As String
End Class
In some cases, you should use the ColumnBase.Binding property instead of the ColumnBase.FieldName property to associate columns with data source fields (for example, to implement custom converters or access collection members). The ColumnBase.Binding property is not a dependency property and cannot be bound. Use the following binding helper to assign a binding to the ColumnBase.Binding property:
using DevExpress.Xpf.Grid;
using System.Windows;
using System.Windows.Data;
namespace ColumnsSample {
public static class BindingHelper {
public static string GetPath(GridColumn obj) {
return (string)obj.GetValue(PathProperty);
}
public static void SetPath(GridColumn obj, string value) {
obj.SetValue(PathProperty, value);
}
public static readonly DependencyProperty PathProperty = DependencyProperty.RegisterAttached("Path", typeof(string), typeof(BindingHelper),
new PropertyMetadata((d, e) => {
if (!string.IsNullOrWhiteSpace(e.NewValue as string))
((GridColumn)d).Binding = new Binding((string)e.NewValue) {
Mode = BindingMode.TwoWay
};
})
);
}
}
Imports DevExpress.Xpf.Grid
Imports System.Windows
Imports System.Windows.Data
Namespace ColumnsSample
Public Module BindingHelper
Public Function GetPath(ByVal obj As GridColumn) As String
Return CStr(obj.GetValue(PathProperty))
End Function
Public Sub SetPath(ByVal obj As GridColumn, ByVal value As String)
obj.SetValue(PathProperty, value)
End Sub
Public ReadOnly PathProperty As DependencyProperty = DependencyProperty.RegisterAttached("Path", GetType(String), GetType(BindingHelper), New PropertyMetadata(Sub(d, e)
If Not String.IsNullOrWhiteSpace(TryCast(e.NewValue, String))
Then CType(d, GridColumn).Binding = New Binding(CStr(e.NewValue)) With {
.Mode = BindingMode.TwoWay
}
End Sub))
End Module
End Namespace
<Window ...
xmlns:dxg="http://schemas.devexpress.com/winfx/2008/xaml/grid"
xmlns:dxci="http://schemas.devexpress.com/winfx/2008/xaml/core/internal">
<DataTemplate x:Key="BindingColumnTemplate">
<dxg:GridColumn local:BindingHelper.Path="{Binding Path=(dxci:DependencyObjectExtensions.DataContext).FieldName, RelativeSource={RelativeSource Self}}"
Header="{Binding Path=(dxci:DependencyObjectExtensions.DataContext).Header, RelativeSource={RelativeSource Self}}"/>
</DataTemplate>
</Window>
public class ViewModel : ViewModelBase {
public ViewModel() {
Columns = new ObservableCollection<Column>() {
// ...
new BindingColumn( SettingsType.Binding, "Cities[0]", "City1" ),
new BindingColumn( SettingsType.Binding, "Cities[1]", "City2" )
};
}
public ObservableCollection<Column> Columns { get; }
}
public class BindingColumn : Column {
public BindingColumn(SettingsType settings, string fieldname, string header): base(settings, fieldname) {
Header = header;
}
public string Header { get; }
}
Public Class ViewModel
Inherits ViewModelBase
Public Sub New()
Columns = New ObservableCollection(Of Column)() From {
' ...
New BindingColumn(SettingsType.Binding, "Cities[0]", "City1"),
New BindingColumn(SettingsType.Binding, "Cities[1]", "City2")}
End Sub
Public ReadOnly Property Columns As ObservableCollection(Of Column)
End Class
Public Class BindingColumn
Inherits Column
Public Sub New(ByVal settings As SettingsType, ByVal fieldname As String, ByVal header As String)
MyBase.New(settings, fieldname)
Me.Header = header
End Sub
Public ReadOnly Property Header As String
End Class
See Also