• XAML Logo

In my efforts to “MVVM-ify” my WPF application as much as possible, I ran into the need to Data Bind the Columns Collection of a DataGrid.

After some searching on StackOverflow, I found a neat solution by David Osborn;

http://stackoverflow.com/questions/3065758/wpf-mvvm-datagrid-dynamic-columns

This solution was in C#, so I converted it to VB;

[fusion_builder_container hundred_percent=”yes” overflow=”visible”][fusion_builder_row][fusion_builder_column type=”1_1″ background_position=”left top” background_color=”” border_size=”” border_color=”” border_style=”solid” spacing=”yes” background_image=”” background_repeat=”no-repeat” padding=”” margin_top=”0px” margin_bottom=”0px” class=”” id=”” animation_type=”” animation_speed=”0.3″ animation_direction=”left” hide_on_mobile=”no” center_content=”no” min_height=”none”][code language=”vb”]
Imports System.Collections.ObjectModel
Imports System.Collections.Specialized
Namespace Controls
Public NotInheritable Class DataGridExtension

Private Sub New()
End Sub

Public Shared Function GetColumns(obj As DependencyObject) As ObservableCollection(Of DataGridColumn)
Return DirectCast(obj.GetValue(ColumnsProperty), ObservableCollection(Of DataGridColumn))
End Function

Public Shared Sub SetColumns(obj As DependencyObject, value As ObservableCollection(Of DataGridColumn))
obj.SetValue(ColumnsProperty, value)
End Sub

Public Shared ReadOnly ColumnsProperty As DependencyProperty = DependencyProperty.RegisterAttached("Columns", _
GetType(ObservableCollection(Of DataGridColumn)), _
GetType(DataGridExtension), _
New UIPropertyMetadata(New ObservableCollection(Of DataGridColumn)(), AddressOf OnDataGridColumnsPropertyChanged))
Private Shared Sub OnDataGridColumnsPropertyChanged(ByVal d As DependencyObject, ByVal e As DependencyPropertyChangedEventArgs)

If d.GetType = GetType(DataGrid) Then

Dim myGrid As DataGrid = TryCast(d, DataGrid)
Dim Columns As ObservableCollection(Of DataGridColumn) = DirectCast(e.NewValue, ObservableCollection(Of DataGridColumn))

If Columns IsNot Nothing Then

myGrid.Columns.Clear()

If Columns IsNot Nothing AndAlso Columns.Count > 0 Then

For Each dataGridColumn As DataGridColumn In Columns
myGrid.Columns.Add(dataGridColumn)
Next

End If

AddHandler Columns.CollectionChanged,
Sub(sender As Object, args As NotifyCollectionChangedEventArgs)

If args.NewItems IsNot Nothing Then

For Each column As DataGridColumn In args.NewItems.Cast(Of DataGridColumn)()
myGrid.Columns.Add(column)
Next

End If

If args.OldItems IsNot Nothing Then

For Each column As DataGridColumn In args.OldItems.Cast(Of DataGridColumn)()
myGrid.Columns.Remove(column)
Next

End If

End Sub
End If

End If

End Sub

End Class

End Namespace
[/code]

Then, in order to use the above, make sure you include the namespace in your XAML resources, for instance;

[/fusion_builder_column][fusion_builder_column type=”1_1″ background_position=”left top” background_color=”” border_size=”” border_color=”” border_style=”solid” spacing=”yes” background_image=”” background_repeat=”no-repeat” padding=”” margin_top=”0px” margin_bottom=”0px” class=”” id=”” animation_type=”” animation_speed=”0.3″ animation_direction=”left” hide_on_mobile=”no” center_content=”no” min_height=”none”][code language=”vb”]
xmlns:w="clr-namespace:MyApplication1.Controls"
[/code]

Then add the following to your DataGrid;

[/fusion_builder_column][fusion_builder_column type=”1_1″ background_position=”left top” background_color=”” border_size=”” border_color=”” border_style=”solid” spacing=”yes” background_image=”” background_repeat=”no-repeat” padding=”” margin_top=”0px” margin_bottom=”0px” class=”” id=”” animation_type=”” animation_speed=”0.3″ animation_direction=”left” hide_on_mobile=”no” center_content=”no” min_height=”none”][code language=”vb”]
w:DataGridExtension.Columns="{Binding Path=DataContext.DataGridColumns, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
[/code]

Also, make sure you set AutoGenerateColumns to False.

Then go ahead and add you property in your View Model;

[/fusion_builder_column][fusion_builder_column type=”1_1″ background_position=”left top” background_color=”” border_size=”” border_color=”” border_style=”solid” spacing=”yes” background_image=”” background_repeat=”no-repeat” padding=”” margin_top=”0px” margin_bottom=”0px” class=”” id=”” animation_type=”” animation_speed=”0.3″ animation_direction=”left” hide_on_mobile=”no” center_content=”no” min_height=”none”][code language=”vb”]

‘ The DataGrid Columns
Property DataGridColumns As ObservableCollection(Of DataGridColumn) Implements Interfaces.iMasterDetailsViewModel(Of T).DataGridColumns
Get
Return _DataGridColumns
End Get
Set(value As ObservableCollection(Of DataGridColumn))
If value.Equals(_DataGridColumns) = False Then
_DataGridColumns = value
OnPropertyChanged("DataGridColumns")
End If
End Set
End Property
[/code]

You can then add a bunch of columns as follows;

[/fusion_builder_column][fusion_builder_column type=”1_1″ background_position=”left top” background_color=”” border_size=”” border_color=”” border_style=”solid” spacing=”yes” background_image=”” background_repeat=”no-repeat” padding=”” margin_top=”0px” margin_bottom=”0px” class=”” id=”” animation_type=”” animation_speed=”0.3″ animation_direction=”left” hide_on_mobile=”no” center_content=”no” min_height=”none”][code language=”vb”]
Dim cols As New ObservableCollection(Of DataGridColumn)
Dim col As New DataGridTextColumn
cols.Add(New DataGridTextColumn With {.Binding = New System.Windows.Data.Binding("Customer_Code"), .Header = "Customer Code", .Width = 100})
cols.Add(New DataGridTextColumn With {.Binding = New System.Windows.Data.Binding("Name"), .Header = "Name", .Width = 250})
DataGridColumns = cols
[/code]

Seems like quite a nice neat solution.

However, do bear in mind that, as Rakshit Bakshi points out in the answer;

“The above code will do the work in general case. However, it will fail when you hide the datagrid and make it visible again. Because the columns property will show that there are 0 columns when they are hidden and as the columns property is changed, the callback will be fired and it will try to add the columns again, but physically columns does exist in the datagrid so the code will fail with an exception saying can’t add duplicate columns.”

[/fusion_builder_column][/fusion_builder_row][/fusion_builder_container]

By |2017-07-24T08:33:19+01:00September 24th, 2012|MVVM, VB.net, WPF|0 Comments

About the Author:

Leave A Comment