• 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”]

 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  

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”]

xmlns:w="clr-namespace:MyApplication1.Controls"  

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”]

w:DataGridExtension.Columns="{Binding Path=DataContext.DataGridColumns, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"  

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”]

     
     ' 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  

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”]

 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  

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+00:00 September 24th, 2012|MVVM, VB.net, WPF|0 Comments

About the Author:

Leave A Comment