RadDataGrid 'StackOverflowException' When Binding to Custom Class

7 posts, 1 answers
  1. Sean
    Sean avatar
    4 posts
    Member since:
    Mar 2019

    Posted 15 Mar Link to this post

    This one is easy to setup when using the 'Windows Template Studio (Universal Windows)' project type. Make use to check 'Telerik Data Grid' of coarse.

    Alter the SampleOrder class to be an Observable class of itself:

    public class SampleOrder : ObservableCollection<SampleOrder>

    Then in the TelerikDataGridPage.xaml edit to add a bound column:

                        <tg:DataGridTemplateColumn Header="Status">
                            <tg:DataGridTemplateColumn.CellContentTemplate>
                                <DataTemplate>
                                    <TextBlock Text="{Binding Status}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
                                </DataTemplate>
                            </tg:DataGridTemplateColumn.CellContentTemplate>
                        </tg:DataGridTemplateColumn>

    Now run and this will generate a StackOverflowException. Obviously the issue is with the bound class being a list of itself. I have my own custom class that behaves like this and I need to bind it the RadDataGrid please help.

    Using just the plain PropertyName 'binding' everything is ok. However, I need to be able to control text alignment, use converters, ...

                        <tg:DataGridTextColumn PropertyName="Status" />     

  2. Answer
    Nasko
    Admin
    Nasko avatar
    727 posts

    Posted 19 Mar Link to this post

    Hello Sean,

    The observed by you exception is not caused by the DataGrid control, but it is actually caused by the Framework itself due to an inaccurate binding. If you replace the Telerik DataGrid with the one provided by the Windows Community Toolkit the exact same exception will be observed. The reason why no exception is observed when PropertyName is used is because internally the PropertyName feature does not use bindings in order to visualize the values of the cells.

    However, you can still achieve the desired by you appearance of the column using another feature of Telerik's DataGrid control - the custom typed column feature. You can simply build a column of your own and make the cell look as desired. Please, check the attached sample that demonstrates that. Also, I still suggest you to reconsider your approach and implementation of the business object (SampleOrder) that is used because any binding made to it will result into a StackOverflowException due to its inheritance from an ObservableCollection.

    I hope the provided information will be helpful for you.

    Please, let me know if you have any questions regarding Telerik controls.

    Regards,
    Nasko
    Progress Telerik
    Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Feedback Portal and vote to affect the priority of the items
  3. Sean
    Sean avatar
    4 posts
    Member since:
    Mar 2019

    Posted 19 Mar in reply to Nasko Link to this post

    Thanks so much for helping me Nasko. Yes shortly after writing my post I did use the stock DataGrid and saw that it also behaves the same. So I knew it wasn't peculiar to Telerik. I will look into your example soon and will also reconsider my implementation as well.
  4. Sean
    Sean avatar
    4 posts
    Member since:
    Mar 2019

    Posted 22 Mar in reply to Nasko Link to this post

    I have gone down the 'Custom Typed Column' route and I really like this. Now because I am using PropertyName the fields are not bound so if a property does update the column will not update. Is there a way to tell a column, row or cell to 'Refresh' in code?

    Thanks Nasko

  5. Nasko
    Admin
    Nasko avatar
    727 posts

    Posted 22 Mar Link to this post

    Hello Sean,

    The DataGrid internally listens for INotifyPropertyChanged and is rebuilding accordingly when PropertyChanged is invoked. Due to the specificity of your scenario calling PropertyChanged will probably result into an exception, because the item is not a pure business object but an ObservableCollection. Again I suggest you to reconsider that approach because it might result into other scenarios that are not supported by the UWP framework.

    Currently, with that implementation the only possible solution for updating the DataGrid I can suggest you is to re-set the ItemSource when changes are made:
    var source = grid.ItemsSource as ObservableCollection<SampleOrder>;
    if (source != null)
    {
        source[0].Status = "This is changed";
        this.grid.ItemsSource = null;
        this.grid.ItemsSource = source;
    }

    Hope this helps.

    Regards,
    Nasko
    Progress Telerik
    Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Feedback Portal and vote to affect the priority of the items
  6. Sean
    Sean avatar
    4 posts
    Member since:
    Mar 2019

    Posted 01 Apr in reply to Nasko Link to this post

    Ok I have made alot of progress and almost there. One last question I  hope. I have edited your example for the sake of answering my question.

    I have created a column based on DataGridTypedColumn. The container is a custom control, MyUserControl, that is nothing more than a TextBlock. I would like to bind a property of my control to the column but I keep getting a XamlParseException. The radiobuttons should just toggle the textalignment from Left to Right. Thanks for all your help Nasko.

     

    TelerikDataGridPage.xaml

    <CODE>

    <Page
        x:Class="RadDataGridUWP_1400756.Views.TelerikDataGridPage"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        Style="{StaticResource PageStyle}"
        xmlns:tg="using:Telerik.UI.Xaml.Controls.Grid"
        xmlns:model="using:RadDataGridUWP_1400756.Models"
        xmlns:views="using:RadDataGridUWP_1400756.Views"
        xmlns:controls="using:Microsoft.Toolkit.Uwp.UI.Controls"
        mc:Ignorable="d">
        <Page.Resources>
            <views:MyConverter x:Key="MyConverter"/>
        </Page.Resources>
        <Grid
            x:Name="ContentArea"
            Margin="{StaticResource MediumLeftRightMargin}">
            <Grid.RowDefinitions>
                <RowDefinition Height="48"/>
                <RowDefinition Height="*"/>
            </Grid.RowDefinitions>

            <TextBlock
                Grid.Row="0"
                x:Uid="TelerikDataGrid_Title"
                Style="{StaticResource PageTitleStyle}" />
            <Grid
                Grid.Row="1" 
                Background="{ThemeResource SystemControlPageBackgroundChromeLowBrush}">
                
                <Grid.RowDefinitions>
                    <RowDefinition Height="*" />
                    <RowDefinition Height="Auto" />
                </Grid.RowDefinitions>

                <tg:RadDataGrid Grid.Row="0" ColumnDataOperationsMode="Flyout" x:Name="grid" ItemsSource="{x:Bind Source}" AutoGenerateColumns="False" >
                    <tg:RadDataGrid.Columns>
                        <tg:DataGridTextColumn PropertyName="OrderId" />
                        <tg:DataGridDateColumn PropertyName="OrderDate" />
                        <tg:DataGridTextColumn PropertyName="Company" />
                        <tg:DataGridTextColumn PropertyName="ShipTo" />
                        <tg:DataGridNumericalColumn PropertyName="OrderTotal" />
                        <views:MyTemplateColumn PropertyName="Status" LeftAlign="{Binding IsChecked, ElementName=LeftRadioButton}" />
                    </tg:RadDataGrid.Columns>
                </tg:RadDataGrid>

                <StackPanel Grid.Row="1" Orientation="Horizontal" HorizontalAlignment="Right">
                    <RadioButton x:Name="LeftRadioButton" IsChecked="True" Margin="10,0,0,0" Content="Align Left" GroupName="align" />
                    <RadioButton Content="Align Right" GroupName="align" />
                </StackPanel>

            </Grid>
        </Grid>
    </Page>

    </CODE>

    MyTemplateColumn:

    <CODE>

    using RadDataGridUWP_1400756.Models;
    using System;
    using Telerik.UI.Xaml.Controls.Grid;
    using Telerik.UI.Xaml.Controls.Grid.Primitives;
    using Windows.UI;
    using Windows.UI.Xaml;
    using Windows.UI.Xaml.Controls;
    using Windows.UI.Xaml.Data;
    using Windows.UI.Xaml.Media;

    namespace RadDataGridUWP_1400756.Views
    {
        public class MyTemplateColumn : DataGridTypedColumn
        {
            private static Type containerType = typeof(MyUserControl);
            private MyUserControl _container;

            public override object GetContainerType(object rowItem)
            {
                return containerType;
            }

            public override object GetEditorType(object item)
            {
                return containerType;
            }

            public override object CreateContainer(object rowItem)
            {
                _container = new MyUserControl();
                return _container;
            }

            public override FrameworkElement CreateEditorContentVisual()
            {
                return null;
            }

            public override void PrepareCell(object container, object value, object item)
            {
                var tb = container as MyUserControl;
                if (tb == null)
                {
                    return;
                }

                if (value == null)
                {
                    tb.Text = string.Empty;
                    return;
                }

                tb.Text = value.ToString();
            }

            public override void PrepareEditorContentVisual(FrameworkElement editorContent, Binding binding)
            {
            }

            public override void ClearEditorContentVisual(FrameworkElement editorContent)
            {
            }

            protected override DataGridFilterControlBase CreateFilterControl()
            {
                return null;
            }

            public bool LeftAlign
            {
                get
                {
                    if (_container == null)
                    {
                        return true;
                    }
                    else
                    {
                        return _container.LeftAlign;
                    }
                }

                set
                {

                    if (_container != null)
                    {
                        _container.LeftAlign = value;
                    }
                }
            }
        }
    }

    </CODE>

    here is my customcontrol MyUserControl.xaml:

    <CODE>

    <UserControl
        x:Class="RadDataGridUWP_1400756.Views.MyUserControl"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="using:RadDataGridUWP_1400756.Views"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d"
        d:DesignHeight="300"
        d:DesignWidth="400">

        <Grid>
            <TextBlock x:Name="MyTextBlock" HorizontalAlignment="Left" />
        </Grid>
    </UserControl>

    </CODE>

    and the xaml.cs

    <CODE>

    using RadDataGridUWP_1400756.Models;
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using System.Runtime.InteropServices.WindowsRuntime;
    using Windows.Foundation;
    using Windows.Foundation.Collections;
    using Windows.UI.Xaml;
    using Windows.UI.Xaml.Controls;
    using Windows.UI.Xaml.Controls.Primitives;
    using Windows.UI.Xaml.Data;
    using Windows.UI.Xaml.Input;
    using Windows.UI.Xaml.Media;
    using Windows.UI.Xaml.Navigation;

    // The User Control item template is documented at https://go.microsoft.com/fwlink/?LinkId=234236

    namespace RadDataGridUWP_1400756.Views
    {
        public sealed partial class MyUserControl : UserControl
        {
            public MyUserControl()
            {
                this.InitializeComponent();
            }

            public string Text
            {
                get => MyTextBlock.Text;
                set => MyTextBlock.Text = value;
            }

            public bool LeftAlign
            {
                get => MyTextBlock.HorizontalAlignment == HorizontalAlignment.Left;

                set
                {

                    if (value == true)
                    {
                        MyTextBlock.HorizontalAlignment = HorizontalAlignment.Left;
                    }
                    else
                    {
                        MyTextBlock.HorizontalAlignment = HorizontalAlignment.Right;
                    }
                }
            }
        }
    }

    <CODE

  7. Nasko
    Admin
    Nasko avatar
    727 posts

    Posted 04 Apr Link to this post

    Hi Sean,

    In order to achieve the desired behavior the properties that are part of the custom UserControl and the custom column should be a dependency one. After that you can easily create a binding to the UserControl's property and switch it as desired through the custom column's property.

    I have modified the project in order to demonstrate the described approach, please check it.

    Hope this helps.

    Regards,
    Nasko
    Progress Telerik
    Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Feedback Portal and vote to affect the priority of the items
Back to Top