Binding Dynamic Columns Without AutoGenerate Columns

I have dataset with dynamic number of columns.  I am using a DataTable to create the data.  I have created an object called TableCellViewModel that contains the data I want for each cell.  The table that gets rendered puts a checkbox in every cell.  In the checkbox checked handler I get the binding context of the checkbox that raised the even.  The binding context is the entire row.  What am I missing?  All of your examples binding to a data table show binding to strings.

                    SfDataGrid dataGrid = new SfDataGrid();
                    dataGrid.GridLoaded += DataGrid_GridLoaded;
                    dataGrid.QueryRowHeight += DataGrid_QueryRowHeight;
                    dataGrid.AutoGenerateColumns = false;
                    dataGrid.Style = Content.FindByName("GridStyle") as Style;
                    dataGrid.VerticalOverScrollMode = VerticalOverScrollMode.None;
                    dataGrid.ItemsSource = tableViewModel.Data;

                    int columnNumber = 0;
                    foreach (KeyValuePair<string, TableColumnWidthType> kvp in tableViewModel.Columns)
                    {
                        GridTemplateColumn gridTemplate = new GridTemplateColumn();
                        gridTemplate.MappingName = $"[{columnNumber}]"; <-- I'm guessing this is the problem
                        gridTemplate.HeaderText = string.IsNullOrWhiteSpace(kvp.Key) ? string.Empty : kvp.Key;
                        gridTemplate.HeaderFontAttribute = FontAttributes.Bold;
                        gridTemplate.HeaderTextAlignment = TextAlignment.Center;
                        gridTemplate.HeaderFont = "SourceSansPro-Bold.ttf#Source Sans Pro";
                        gridTemplate.HeaderCellTextSize = 14;
                        gridTemplate.CellTemplate = (DataTemplate)Content.FindByName("CellTemplate");
                        gridTemplate.LineBreakMode = LineBreakMode.CharacterWrap;

                        if (kvp.Value == TableColumnWidthType.Header)
                        {
                            gridTemplate.ColumnSizer = ColumnSizer.SizeToHeader;
                        }
                        else
                        {
                            gridTemplate.Width = 200;
                        }

                        dataGrid.Columns.Add(gridTemplate);
                        columnNumber++;
                    }

Here is the cell template.  I want to either show a label or a checkbox.  Every cell shows a checkbox, which makes sense if the template is binding to the entire row.
            <DataTemplate x:Key="CellTemplate" x:Name="CellTemplate">
                <Grid Margin="1,1,1,0" BackgroundColor="{ DynamicResource DataGridBackgroundColor }">
                    <Label Grid.Row="0" Grid.Column="0"
                        Text="{Binding CellText}"
                        IsVisible="{Binding CellType, Converter={StaticResource TextCellVisibility}}"
                        IsEnabled="{Binding CanEdit}"
                        Margin="3,0,0,0"
                        TextColor="{ DynamicResource DataGridCellTextColor }"
                        VerticalOptions="Center"
                        HorizontalOptions="Start">
                        <Label.GestureRecognizers>
                            <TapGestureRecognizer Tapped="CellText_Tapped"/>
                        </Label.GestureRecognizers>
                    </Label>
                    <CheckBox Grid.Row="0" Grid.Column="0"
                        IsChecked="{Binding CheckboxValue}"
                        IsVisible="{Binding CellType, Converter={StaticResource CheckBoxCellVisibility}}"
                        IsEnabled="{Binding CanEdit}"
                        CheckedChanged="CellCheckBox_Checked"
                        Color="{ DynamicResource DataGridCellTextColor }"
                        HorizontalOptions="Center" />
                </Grid>
            </DataTemplate>

10 Replies 1 reply marked as answer

KK Karthikraja Kalaimani Syncfusion Team July 20, 2020 12:55 PM UTC

Hi Nick,

Currently, we are validating your requirement. We will update further details on or before 22nd July 2020.  We appreciate your patience until then.

Regards,
Karthik Raja



KK Karthikraja Kalaimani Syncfusion Team July 22, 2020 09:44 AM UTC

Hi Nick,

The data model values are in Array type in DataTable, so you can directly bind the array in the elements like below highlighted code snippet.
Code snippet :

 
[XAML]
<
ContentPage.Resources> 
        <ResourceDictionary> 
            <DataTemplate x:Key="CellTemplate" x:Name="CellTemplate"> 
                <Grid Margin="1,1,1,0"  > 
                    <Grid.RowDefinitions> 
                        <RowDefinition Height="Auto"></RowDefinition> 
                    </Grid.RowDefinitions> 
                    <Grid.ColumnDefinitions> 
                        <ColumnDefinition Width="100"></ColumnDefinition> 
                        <ColumnDefinition Width="100"></ColumnDefinition> 
                    </Grid.ColumnDefinitions> 
                    <Label Grid.Row="0" Grid.Column="0" 
                        Text="{Binding [1]}" 
                        Margin="3,0,0,0" 
                        VerticalOptions="Center" 
                        HorizontalOptions="Start"> 
                        <Label.GestureRecognizers> 
                            <TapGestureRecognizer Tapped="TapGestureRecognizer_Tapped"/> 
                        </Label.GestureRecognizers> 
                    </Label> 
                    <CheckBox Grid.Row="0" Grid.Column="1" 
                        IsChecked="{Binding [2]}" 
                        CheckedChanged="CheckBox_CheckedChanged" 
                        HorizontalOptions="Center" /> 
                </Grid> 
            </DataTemplate> 
        </ResourceDictionary> 
    </ContentPage.Resources>

[.CS]
            SfDataGrid dataGrid = new SfDataGrid(); 
            dataGrid.AutoGenerateColumns = false; 
            dataGrid.VerticalOverScrollMode = VerticalOverScrollMode.None; 
            dataGrid.ItemsSource = viewModel.Data; 
            dataGrid.ColumnSizer = ColumnSizer.Auto; 
 
            int columnNumber = 0; 
            for(int i=0;i<2;i++) 
            { 
                GridTemplateColumn gridTemplate = new GridTemplateColumn(); 
                gridTemplate.MappingName = $"[{columnNumber}]";
                gridTemplate.HeaderText = viewModel.Data.Columns[columnNumber].ColumnName;
 
                gridTemplate.HeaderFontAttribute = FontAttributes.Bold; 
                gridTemplate.HeaderTextAlignment = TextAlignment.Center; 
                gridTemplate.HeaderCellTextSize = 14; 
                gridTemplate.BindingContextChanged += GridTemplate_BindingContextChanged;   
                gridTemplate.CellTemplate = (DataTemplate)Content.FindByName("CellTemplate"); 
                gridTemplate.LineBreakMode = LineBreakMode.CharacterWrap; 
 
                dataGrid.Columns.Add(gridTemplate); 
                columnNumber++; 
            } 
 
            this.stack.Children.Add(dataGrid); 

Regards,
Karthik Raja



NI Nick July 22, 2020 02:18 PM UTC

I want the data in column 1 of the DataTable to be the binding context for column 1 of the SFDataGrid.  I want the data in column 2 of the DataTable to be the binding context for column 2 of the SFDataGrid.  Since I have an unknown number of columns I can't just create templates for each column.

Each cell of the DataTable contains the same object type, and each cell in the SFDataGrid will contain either a Checkbox or a label.


KK Karthikraja Kalaimani Syncfusion Team July 23, 2020 12:06 PM UTC

Hi Nick.

You need to create each cell template for each column to achieve your requirement in Code behind like the below high-lighted code snippet. For more details please refer to the below code snippet.

Code  snippet :

 
SfDataGrid dataGrid = new SfDataGrid(); 
            dataGrid.AutoGenerateColumns = false; 
            dataGrid.VerticalOverScrollMode = VerticalOverScrollMode.None; 
            dataGrid.ItemsSource = viewModel.Data; 
            dataGrid.ColumnSizer = ColumnSizer.Auto; 
 
            int columnNumber = 0; 
            for(int i=0;i<2;i++) 
            { 
                GridTemplateColumn gridTemplate = new GridTemplateColumn(); 
                gridTemplate.MappingName = $"[{columnNumber}]"; 
                gridTemplate.HeaderText = viewModel.Data.Columns[columnNumber].ColumnName; 
                gridTemplate.HeaderFontAttribute = FontAttributes.Bold; 
                gridTemplate.HeaderTextAlignment = TextAlignment.Center; 
                gridTemplate.HeaderCellTextSize = 14; 
                gridTemplate.CellTemplate = GetTemplate(i); 
                gridTemplate.LineBreakMode = LineBreakMode.CharacterWrap; 
                dataGrid.Columns.Add(gridTemplate); 
                columnNumber++; 
            } 
 
            this.stack.Children.Add(dataGrid); 
        } 
 
        private DataTemplate GetTemplate(int index) 
        { 
            DataTemplate cellTemplate = new DataTemplate(() => 
            { 
                Label label; 
                label = new Label(); 
                label.SetBinding(Label.TextProperty, new Binding($"[{index}]")); 
                return label; 
            }); 
 
            return cellTemplate; 
        } 
 

Sample link : https://www.syncfusion.com/downloads/support/directtrac/general/ze/DataGridSample1373350445-95908066.zip

Please let us know if you need any further assistance from this.

Regards,
Karthik Raja
 


Marked as answer

NI Nick July 24, 2020 03:54 AM UTC

This seems to be working.  I'm running into an issue in my QueryRowHeight event handler now.  It is throwing an out of range exception if there is more than one table on a page.  The handler is pretty simple.  The exception is happening in the GetRowHeight call.
            SfDataGrid dataGrid = sender as SfDataGrid;
            if (e.RowIndex > 0)
            {
                e.Height = SfDataGridHelpers.GetRowHeight(dataGrid, e.RowIndex);
                e.Handled = true;
            }


I'm also seeing odd behavior on the checkboxes.  When the table loads the CheckedChanged event is firing.  Any idea why that would be?


KK Karthikraja Kalaimani Syncfusion Team July 27, 2020 08:07 AM UTC

Hi Nick,

We have checked the reported issue “Out Range expection thrown when call GetRowHeight method” with more than one SfDatGrid. The GetRowHeight method working fine as expected without exception. We have attached the tested sample for your reference.

Sample link :  https://www.syncfusion.com/downloads/support/directtrac/general/ze/DataGridSample2062071549.zip


Please check the sample and let us know if you still facing the same issue? If not, please modify the sample based on your scenario and revert us back. 

Regarding “odd behavior on the CheckBox”

We have checked with single row with IsChecked value as true and we saw that the CheckedChanged event is fired continuously with value as true and false. So, could you please confirm whether or not are you facing the same behavior on your side?. If not, please provide a code to check the behavior of CheckedChanged event. It will help us to give better solution earlier.


Regards,
Karthik Raja
 



NI Nick July 31, 2020 03:29 AM UTC

I am seeing the CheckChanged event handler getting fired with both true and false as well.  It doesn't seem to match anything in the view model.


KK Karthikraja Kalaimani Syncfusion Team August 3, 2020 01:26 PM UTC

Hi Nick,

We have checked the behavior of CheckedChanged event. In our source we create content and dispose of for the DataTemplate in two places which is used to calculate the height and width of the element. So, only it triggers more than 4 times for a cell.

Regards,
Karthik Raja
 



NI Nick August 7, 2020 03:13 AM UTC

I don't understand what you're saying here.


KK Karthikraja Kalaimani Syncfusion Team August 10, 2020 01:19 PM UTC

Hi Nick,

In our source, we create a view for the DataTemplate by using Xamarin forms method DataTemplate.CreateContent() and dispose of the view by setting the null value to the view. So, the CheckedChanged event gets triggered with true value and false value after that method call and the view set to null. We are doing these operations in two places while measuring the height and width of GridTemplateColumn.

Regards,
Karthik Raja
 


Loader.
Up arrow icon