Unexpected Behavior with SfGrid.DataSource

I am experiencing some unexpected behavior with a simple grid configuration I am hoping to track down.

To start, here is my model:

```
public sealed class View
{
public View(Guid identity, DisplayName name, DateTimeOffset created)
{
Identity = identity;
Name     = name;
Created  = created;
}

public Guid Identity { get; }

public DisplayName Name { get; }

public DateTimeOffset Created { get; }

public void Deconstruct(out Guid identity, out DisplayName name, out DateTimeOffset created)
{
identity = Identity;
name     = Name;
created  = Created;
}
}

public sealed class DisplayName
{
public DisplayName(string provider, string name)
{
Provider = provider;
Name     = name;
}

public string Provider { get; }

public string Name { get; }

public void Deconstruct(out string provider, out string name)
{
provider = Provider;
name     = Name;
}
}

public sealed class Entry
{
public bool Enabled { get; set; }

public float Amount { get; set; } = .1f;

public ICollection Views { get; } = new List();
}
     
```

Here is my .razor file:

```
           
           
               
               
               
                   
               
           
               
@code{
        [Parameter]
public Entry Input { get; set; } = default!;
}
```

When I add to the `Input.Views` collection and `StateHasChanged` the `SfGrid` does not update and refresh with the new state.  However, if I change the SfGid to `DataSource="@Input.Views.ToList()"` the state changes as expected and the Input.Views is updated properly.  However, this is unexpected and now I am binding to a copy of the view collection and not directly to the view collection itself.  (Additionally, there is now another allocation to memory due to the new list.)  

Is there a property/concept here that I am overlooking?  I would not expect to have to call `ToList` to update state properly and would prefer to bind directly to `Input.Views`.

Thank you for any assistance you can provide!



9 Replies 1 reply marked as answer

MI Mike-E replied to Michael March 15, 2021 08:43 AM UTC

I am experiencing some unexpected behavior with a simple grid configuration I am hoping to track down.

To start, here is my model:

```
public sealed class View
{
public View(Guid identity, DisplayName name, DateTimeOffset created)
{
Identity = identity;
Name     = name;
Created  = created;
}

public Guid Identity { get; }

public DisplayName Name { get; }

public DateTimeOffset Created { get; }

public void Deconstruct(out Guid identity, out DisplayName name, out DateTimeOffset created)
{
identity = Identity;
name     = Name;
created  = Created;
}
}

public sealed class DisplayName
{
public DisplayName(string provider, string name)
{
Provider = provider;
Name     = name;
}

public string Provider { get; }

public string Name { get; }

public void Deconstruct(out string provider, out string name)
{
provider = Provider;
name     = Name;
}
}

public sealed class Entry
{
public bool Enabled { get; set; }

public float Amount { get; set; } = .1f;

public ICollection Views { get; } = new List();
}
     
```

Here is my .razor file:

```
           
           
               
               
               
                   
               
           
               
@code{
        [Parameter]
public Entry Input { get; set; } = default!;
}
```

When I add to the `Input.Views` collection and `StateHasChanged` the `SfGrid` does not update and refresh with the new state.  However, if I change the SfGid to `DataSource="@Input.Views.ToList()"` the state changes as expected and the Input.Views is updated properly.  However, this is unexpected and now I am binding to a copy of the view collection and not directly to the view collection itself.  (Additionally, there is now another allocation to memory due to the new list.)  

Is there a property/concept here that I am overlooking?  I would not expect to have to call `ToList` to update state properly and would prefer to bind directly to `Input.Views`.

Thank you for any assistance you can provide!



Boo, editing my comment scrubbed the razor file content!
Let's see if I can restore it here:

```
<SfGrid TValue="View" AllowPaging="false" AllowFiltering="false" AllowSorting="true" DataSource="@Input.Views">
            <GridPageSettings PageCount="10" />
            <GridColumns>
                <GridColumn Field="Name.Name" HeaderText="Name" />
                <GridColumn Field="Name.Provider" HeaderText="Provider" />
                <GridColumn Field="Created" HeaderText="Created" Type="ColumnType.DateTime" Width="250px">
                    <Template Context="content">
                        @{
                            var view = content.To<View>();
                            @($"{view.Created:f} UTC")
                        }
                    </Template>
                </GridColumn>
            </GridColumns>
        </SfGrid>
```


MI Mike-E March 15, 2021 08:59 AM UTC

I should also note that I am adding an item to `Input.Views` via a dialog control not shown in the previous content.  I did not provide it as I did not feel it is relevant to the issue I am describing, which is that `DataSource="@Input.Views"` does not work, while `DataSource="@Input.Views.ToList()"` does work.  Hope that makes sense. :)



RS Renjith Singh Rajendran Syncfusion Team March 19, 2021 02:54 AM UTC

Hi Mike-E, 

Greetings from Syncfusion support. 

We tried reproducing the reported problem by binding ICollection as data to Grid. But we could not face the reported problem from our side. We are attaching the sample which we have tried from our side for your reference, please download the sample from the link below, 
 
If you are still facing difficulties, then the following details would be helpful for us to proceed further. 

  1. Share with us a simple sample, which would be helpful for us to validate based on your scenario.
  2. If possible, reproduce the problem with the above attached sample and share with us for further analysis.
  3. Share the video demo showing the problem you are facing.

And also please bind the “OnActionFailure” event to Grid. This event will be triggered for every failure in Grid. Please share the details you get in the “args” of this event handler for further analysis.  

Regards, 
Renjith R 



MI Mike-E May 6, 2022 07:17 AM UTC

Hi @Renjith,


Thank you for your reply and pardon my late reply here.  I encountered this again with the SfListBox control and did some searching to see that I have already reported this here.


The problem is not during the `OnInitialized` event, but after a button click.  Here is the code in its entirety to reproduce:


```

@page "/"


@using Syncfusion.Blazor.Grids

@using Syncfusion.Blazor.Buttons


<SfGrid TValue="View" AllowPaging="false" AllowFiltering="false" AllowSorting="true" DataSource="@Views">

    <GridPageSettings PageCount="10" />

    <GridColumns>

        <GridColumn Field="Name.Name" HeaderText="Name" />

        <GridColumn Field="Name.Provider" HeaderText="Provider" />

        <GridColumn Field="Created" HeaderText="Created" Type="ColumnType.DateTime" Width="250px">

            @*<Template Context="content">

                @{

                    var view = content.To<View>();

                    @($"{view.Created:f} UTC")

                }

            </Template>*@

        </GridColumn>

    </GridColumns>

</SfGrid>


<SfButton OnClick="OnClick">Click Me</SfButton>


@code{

    protected override void OnInitialized()

    {


    }

    public ICollection<View> Views { get; } = new List<View>();

    public sealed class View

    {

        public View()

        {


        }

        public View(Guid identity, DisplayName name, DateTimeOffset created)

        {

            Identity = identity;

            Name = name;

            Created = created;

        }


        public Guid Identity { get; set; }


        public DisplayName Name { get; set; }


        public DateTimeOffset Created { get; set; }


        public void Deconstruct(out Guid identity, out DisplayName name, out DateTimeOffset created)

        {

            identity = Identity;

            name = Name;

            created = Created;

        }

    }


    public sealed class DisplayName

    {

        public DisplayName()

        {


        }

        public DisplayName(string provider, string name)

        {

            Provider = provider;

            Name = name;

        }


        public string Provider { get; set; }


        public string Name { get; set; }


        public void Deconstruct(out string provider, out string name)

        {

            provider = Provider;

            name = Name;

        }

    }


    Task OnClick() {

        Views.Add(new View() { Identity = new Guid(), Name = new DisplayName() { Provider = "Nancy", Name = "Fuller" }, Created = new DateTimeOffset() });

        StateHasChanged();

        return Task.CompletedTask;

    }


}

```


Note that after clicking the `Click Me` button the grid does not update with the new collection, despite having called `StateHasChanged`.


Any further assistance would be appreciated.


Thank you,

Michael



RS Renjith Singh Rajendran Syncfusion Team May 9, 2022 08:50 AM UTC

Hi Michael,


We suggest you to call the Refresh() method of Grid to overcome the reported scenario. Please refer and use the code below,


 

<SfGrid @ref="GridRef" TValue="View">

     ...

</SfGrid>

 

<SfButton OnClick="OnClick">Click Me</SfButton>

 

    SfGrid<View> GridRef;

 

    Task OnClick() {

        Views.Add(new View() { Identity = new Guid(), Name = new DisplayName() { Provider = "Nancy", Name = "Fuller" }, Created = new DateTimeOffset() });

        StateHasChanged();

        GridRef.Refresh();

        return Task.CompletedTask;

    }

 


Please get back to us if you need further assistance.


Regards,

Renjith R


Marked as answer

MI Mike-E May 9, 2022 11:09 AM UTC

OK @Renjith thank you for the update.  That was my understanding as well, but my expectation is that if I am binding on a collection that it would be refreshed without any intervention.


Note that the Refresh call is now asynchronous and can be done with the following with the latest version:


Task OnClick() {

        Views.Add(new View() { Identity = new Guid(), Name = new DisplayName() { Provider = "Nancy", Name = "Fuller" }, Created = new DateTimeOffset() });

        StateHasChanged();

        return GridRef.Refresh();

    }



RS Renjith Singh Rajendran Syncfusion Team May 10, 2022 01:32 PM UTC

Hi Mike-E,


When binding List/ICollection data to grid, then to update the dynamic changes in Grid it is a must to call the Refresh method. If you don’t want to call Refresh method, then you can bind data to Grid as ObservableCollection. Please refer the documentation and online sample demos showing CRUD operation with Observable Collection as data in Grid.

Documentation : https://blazor.syncfusion.com/documentation/datagrid/data-binding/#observable-collection

Online demo : https://blazor.syncfusion.com/demos/Grid/ObservableBinding?theme=bootstrap4


Please get back to us if you need further assistance.


Regards,

Renjith R



MI Mike-E May 10, 2022 04:02 PM UTC

Ah!  ObservableCollection!  A blast from the past there, @Renjith. :)  That makes a lot more sense now.

Thank you for the extra context/information.



RS Renjith Singh Rajendran Syncfusion Team May 11, 2022 05:11 AM UTC

Hi Mike-E,


Thanks for your update. Please get back to us if you need further assistance.


Regards,

Renjith R


Loader.
Up arrow icon