Quantcast
Channel: Windows Presentation Foundation (WPF) forum
Viewing all articles
Browse latest Browse all 18858

Binding to data source with dynamically created columns in DataGrid

$
0
0

Hi

Like many others, I have a need for some dynamically created columns in a DataGrid within a WPF 4.5 project. As well as some static columns, new columns can be created or removed via the interface depending on user requirements. The project takes a View-Model-first approach and uses the Caliburn Micro framework (v1.4.1).

To tackle the problem of dynamic columns, I’m using an Observable Collection of DataGrid columns, per http://stackoverflow.com/questions/3065758/wpf-mvvm-datagrid-dynamic-columns. I then create (or remove) the columns in the View Model.

In order to overcome the problem of how to bind the cells within the new columns to the backing data context, I’ve been experimenting with inheriting from the DataGridTemplateColumn thus:

Public Class DataGridBoundTemplateColumn
    Inherits DataGridTemplateColumn

    Public Property BindingPath() As String

    Protected Overrides Function GenerateElement(ByVal cell As DataGridCell, ByVal dataItem As Object) As FrameworkElement
        Dim element = MyBase.GenerateElement(cell, dataItem)
        element.SetBinding(ContentPresenter.ContentProperty, New Binding(Me.BindingPath))
        Return element
    End Function

    Protected Overrides Function GenerateEditingElement(ByVal cell As DataGridCell, ByVal dataItem As Object) As FrameworkElement
        Dim element = MyBase.GenerateEditingElement(cell, dataItem)
        element.SetBinding(ContentPresenter.ContentProperty, New Binding(Me.BindingPath))
        Return element
    End Function

End Class

This is a VB translation of the work presented at http://stackoverflow.com/questions/14000873/define-datatemplate-in-xaml-instantiate-and-modify-in-code. 

When it comes time to add a column, I do the following:

Dim myCol As New DataGridBoundTemplateColumn
Dim viewTemplate = CType(Application.Current.TryFindResource("EnvTemplate"), DataTemplate)
Dim editTemplate = CType(Application.Current.TryFindResource("EnvEditingTemplate"), DataTemplate)

EnvVarsDT.Columns.Add(EnvName)

With myCol
  .Header = EnvName
  .Width = 100
  .CellTemplate = viewTemplate
  .CellEditingTemplate = editTemplate
  .BindingPath = EnvName
End With

DataGridColumns.Add(myCol)

The XAML code for the DataGrid is simply:

<DataGrid Name="EnvVarsDT" Grid.Row="1" CanUserAddRows="False" Margin="5" AutoGenerateColumns="False"
jas:DataGridExtension.Columns="{Binding Path=DataContext.DataGridColumns, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"/>

… where jas: is the namespace for my project.  EnvVarsDT is a DataTable within the View Model and the DataGrid is named the same, following Caliburn conventions. So far so good; the columns get added, with the correct headers.

Now to the problem.  Using a templated approach (either the custom DataGridBoundTemplateColumn or a plain vanilla DataGridTemplateColumn), I am unable to get the bindings to work. No binding errors show up in the Immediate window (or Output for that matter). Meanwhile Snoop shows the DataContext property on each cell in the column to be empty. User edits get lost as soon as they tab off the cell, and the values are not available via the DataTable.

I can however get the bindings working if I use a regular DataGridTextColumn:

Dim myCol As New DataGridTextColumn
With myCol
   .Binding = New Binding(EnvName)
   .Width = 100
   .CellStyle = CType(Application.Current.TryFindResource("CellStyle"), Style)
   .Header = EnvName
End With

Now each cell in the dynamically added columns is properly bound and user edits are retained.

But I need the construction of the cells to vary according to another data value in the row, so I don’t think I can use a DataGridTextColumn. The editing template referenced in the template column approach specifies a button in one case and a textbox otherwise (and these show up properly, albeit with no binding value, when using a DataGridTemplateColumn). I have experimented just with plain textboxes so as to take a complicated data template out of the equation, but to no avail. Using a DataGridTemplateColumn, the editing controls are revealed and one can enter text, but the data is lost on tabbing away. Using the DataGridBoundTemplateColumn, the controls are not revealed when attempting to edit – it is as if the cells were Read Only.

The DataTemplates I’m using are, for simple test purposes, as follows:

<DataTemplate x:Key="EnvTemplate"><TextBlock Text="{Binding Path=.}"/></DataTemplate><DataTemplate x:Key="EnvEditingTemplate"><TextBox Text="{Binding Path=.}"/></DataTemplate>

I’ve tried playing with setting the DataContext in these templates, eg DataContext="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type data:DataRowView}}}",but without success.

I feel close to a working solution, with the binding on a dynamically added DataGridTemplate column being the outstanding matter. Any guidance as to things to try will be much appreciated.

With thanks and regards

Sebastian Crewe








Viewing all articles
Browse latest Browse all 18858

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>