-2

I liked this approach to the solution, except that it will not work with virtualization.

Here's the relevant code from that post, abbreviated:

protected override FrameworkElement GenerateElement(DataGridCell cell, object dataItem)
{
    DataRowView dataRow = (dataItem as DataRowView);

    object cellData = dataRow[cell.Column.DisplayIndex];

    var contentHost = new ContentControl() { Content = cellData };

    contentHost.ContentTemplate = (DataTemplate)SomeResourceDictionary["SomeResourceKey"];

    return contentHost;
}

This method is called by the DataGrid for every cell created/rendered. If virtualization is enabled, then the FrameworkElement created here may be recycled, i.e. used again.

The problem: using the information stored in cell and dataItem the content is determined and a direct reference is passed to the created ContentControl. If this FrameworkElement is reused, it will reference the same content - regardlesss of which row it is being rendered for.

Result: during (quick) scrolling the content displayed in the grid rows will randomly be mixed up.

My question: using cell.Column.DisplayIndex can the ContentControl.Content binding be set without including a direct reference to the desired content? Maybe, as if I defined it in Xaml something like this: "{Binding Path=dataRow[0]}" - but that also doesn't seem quite right.

Any suggestions greatly appreciated!

@BionicCode:

There are two challenges addressed here:

  1. Binding a DataGrid to a source with dynamic columns.
  2. Allow a DataTemplate to be defined for each dynamically generated column.

The first issue is covered quite well in the post linked above: create a DataTable from the original source and bind the DataGrid to that. The difficult part of the second issue - and why DataGridTemplateColumns cannot simply be used - is that DataGridTemplateColumns receive the entire row item as data context, i.e. to access the correct data, the template would have to know which column it is being used in.

This is where the code solution shown above comes in, by manually generating the FrameworkElement and "statically" assigning the desired content.

Luckily, the fix is rather simple, see my answer below.

mike
  • 1,627
  • 1
  • 14
  • 37
  • It is not clear what exactly you are trying to achieve. – BionicCode Oct 10 '21 at 11:15
  • If I follow correctly I would be more likely to build a collection of datagrid column definitions. Xamlreader.parse a string for each into a column. – Andy Oct 10 '21 at 11:43
  • @andy could you explain what you mean with "Xamlreader.parse a string for each into a column"? – mike Oct 10 '21 at 17:34
  • @andy if you review my answer, how do you think the performance of your approach would compare to mine? – mike Oct 10 '21 at 20:44
  • Var newcolumndefinition=Xamlreader.parse(thestring); allows you to build ui as a string and parse it into a ui object. Instead of fixed columns in a datagrid defined at compile time, you're doing the same dynamically at run time. I had a repo on technet illustrating this, but ms binned the technet wiki repositories. Performance should be far better. – Andy Oct 11 '21 at 15:25
  • @andy you mean parsing the string is faster than creating them in code? or do i not quite understand? what xaml-string would i use? – mike Oct 11 '21 at 22:26
  • I mean my suggested way is faster than yours. Whatever string matches what you need for your column. – Andy Oct 12 '21 at 16:57
  • @ andy if i understand you correctly and both our solutions ultimately create the same column instances -- why would xaml parsing be quicker than creating instances directly in code? – mike Oct 12 '21 at 19:40

1 Answers1

0

I knew the solution had to be close and, luckily, it was. In the code above replace the following line...

var contentHost = new ContentControl() { Content = cellData };

...with these:

var contentHost = new ContentControl();
BindingOperations.SetBinding(contentHost, ContentControl.ContentProperty, new Binding("[" + cell.Column.DisplayIndex + "]"));
            

Now the ContentControl can dynamically bind to the correct column and virtualization works. At least EnableRowVirtualization, I haven't tested EnableColumnVirtualization, yet.

mike
  • 1,627
  • 1
  • 14
  • 37