ObservableCollection does not rerender if Count remains the same

Hi,

I am facing the following issue:
When loading the data in an observable collection, and changing the data, it only changes the dropdown when a new record is added, not when the count stays the same.
In the example below, you will see an output of the collection, which is not the same as the dropdown when clicking the "Change Values" button, but is again in sync when clicking the "Add Value" button.

Am I missing a parameter/property/event, is this by design or is this an error?

Thanks for your assistance!


Best regards,

Mike

-----

@page "/DropdownObservableWithSameCount"
@using Syncfusion.Blazor.DropDowns
@using Syncfusion.Blazor.Buttons
@using System.Collections.ObjectModel;

<div>
    <SfDropDownList @ref="DDLObject" TItem="RecordModel" TValue="int" @bind-Value="@id" DataSource="@records">
        <DropDownListFieldSettings Text="@nameof(RecordModel.Text)" Value="@nameof(RecordModel.Id)" />
    </SfDropDownList>

    <SfButton OnClick="OnChangeButtonClick">Change values</SfButton>
    <SfButton OnClick="OnAddButtonClick">Add Value</SfButton>
</div>
<div>
    Id: @id
</div>
<div>
    Collection: @(Newtonsoft.Json.JsonConvert.SerializeObject(records))
</div>

@code
{
    SfDropDownList<int, RecordModel> DDLObject;

    private int id;

    public class RecordModel
    {
        public int Id { get; set; }
        public string Text { get; set; }
    }

    private ObservableCollection<RecordModel> records = new ObservableCollection<RecordModel>() {
        new RecordModel(){ Id= 1, Text= "A" },
        new RecordModel(){ Id= 2, Text= "B" },
        new RecordModel(){ Id= 3, Text= "C" },
        new RecordModel(){ Id= 4, Text= "D" },
        new RecordModel(){ Id= 5, Text= "E" },
        new RecordModel(){ Id= 6, Text= "F" },
        new RecordModel(){ Id= 7, Text= "G" }
     };

    private void OnAddButtonClick(MouseEventArgs args)
    {
        records.Add(new RecordModel { Id = records.Max(x => x.Id) + 1, Text = (records.Max(x => x.Id) + 1).ToString() });
    }

    private async Task OnChangeButtonClick(MouseEventArgs args)
    {
        var list = new List<RecordModel>();
        foreach (var item in records)
            list.Add(new RecordModel { Id = item.Id + records.Count, Text = (item.Text + item.Text) });

        foreach (var item in list)
            if (!records.Any(x => x.Id == item.Id))
                records.Insert(records.Count, item);

        var count = records.Count;
        for (var i = 0; i < count; i++)
            if (!list.Any(x => x.Id == records[i].Id))
            {
                records.RemoveAt(i);
                i--;
                count--;
            }

        await InvokeAsync(() => StateHasChanged());
    }

}


7 Replies

SP Sureshkumar P Syncfusion Team September 6, 2020 02:59 PM UTC

Hi Michael, 
 
Greetings from Syncfusion support. 
 
Based on your shared information we have created and checked the sample. We suspect that you have changed the data source with same refence variable. We suggest you change the data source with different reference variable as like below code example.  
 
Please find the code example.  
@using Syncfusion.Blazor.Buttons 
@using System.Collections.ObjectModel; 
 
<div> 
    <SfDropDownList @ref="DDLObject" TItem="RecordModel" TValue="int" @bind-Value="@id" DataSource="@records"> 
        <DropDownListFieldSettings Text="@nameof(RecordModel.Text)" Value="@nameof(RecordModel.Id)" /> 
    </SfDropDownList> 
 
    <SfButton OnClick="OnChangeButtonClick">Change values</SfButton> 
    <SfButton OnClick="OnAddButtonClick">Add Value</SfButton> 
</div> 
<div> 
    Id: @id 
</div> 
<div> 
    Collection: @(Newtonsoft.Json.JsonConvert.SerializeObject(records)) 
</div> 
 
@code 
{ 
    SfDropDownList<intRecordModel> DDLObject; 
 
    private int id; 
 
    public class RecordModel 
    { 
        public int Id { get; set; } 
        public string Text { get; set; } 
    } 
 
    private ObservableCollection<RecordModel> records = new ObservableCollection<RecordModel>() { 
        new RecordModel(){ Id= 1, Text= "A" }, 
        new RecordModel(){ Id= 2, Text= "B" }, 
        new RecordModel(){ Id= 3, Text= "C" }, 
        new RecordModel(){ Id= 4, Text= "D" }, 
        new RecordModel(){ Id= 5, Text= "E" }, 
        new RecordModel(){ Id= 6, Text= "F" }, 
        new RecordModel(){ Id= 7, Text= "G" } 
     }; 
 
    private ObservableCollection<RecordModel> records1 = new ObservableCollection<RecordModel>() { 
        new RecordModel(){ Id= 1, Text= "A1" }, 
        new RecordModel(){ Id= 2, Text= "B1" }, 
        new RecordModel(){ Id= 3, Text= "C1" }, 
        new RecordModel(){ Id= 4, Text= "D1" }, 
        new RecordModel(){ Id= 5, Text= "E1" }, 
        new RecordModel(){ Id= 6, Text= "F1" }, 
        new RecordModel(){ Id= 7, Text= "G1" } 
     }; 
 
    private void OnAddButtonClick(MouseEventArgs args) 
    { 
        records.Add(new RecordModel { Id = records.Max(x => x.Id) + 1, Text = (records.Max(x => x.Id) + 1).ToString() }); 
    } 
 
    private async Task OnChangeButtonClick(MouseEventArgs args) 
    { 
        this.records = this.records1; 
        //var list = new List<RecordModel>(); 
        //foreach (var item in records) 
        //    list.Add(new RecordModel { Id = item.Id + records.Count, Text = (item.Text + item.Text) }); 
 
        //foreach (var item in list) 
        //    if (!records.Any(x => x.Id == item.Id)) 
        //        records.Insert(records.Count, item); 
 
        //var count = records.Count; 
        //for (var i = 0; i < count; i++) 
        //    if (!list.Any(x => x.Id == records[i].Id)) 
        //    { 
        //        records.RemoveAt(i); 
        //        i--; 
        //        count--; 
        //    } 
 
        //await InvokeAsync(() => StateHasChanged()); 
    } 
 
} 
 
  
 
 
Regards, 
Sureshkumar P 



MI Michael September 7, 2020 09:22 AM UTC

Thanks for the reply.
I am then wondering what is the use of having ObservableCollection support?
How come the values are updated when there is a new item added, but not when you change the values of the collection itself?

Thanks for letting me know.

Best regards,

Mike


SP Sureshkumar P Syncfusion Team September 10, 2020 02:19 PM UTC

Hi Michael, 
 
Sorry for the inconvenience caused. 
 
We have validated your attached code example; you have created the list instead of ObservableCollection when change the value. That is the reason the delay when update the datasource in the dropdownlist component. We suggest you top change the List<RecordModel>() into ObservableCollection<RecordModel>(records) when copy the records.  
 
Please find the modified code example here: 
<div> 
    <SfDropDownList @ref="DDLObject" TItem="RecordModel" TValue="int?" @bind-Value="@id" DataSource="@records"> 
        <DropDownListFieldSettings Text="@nameof(RecordModel.Text)" Value="@nameof(RecordModel.Id)" /> 
    </SfDropDownList> 
 
    <SfButton OnClick="OnChangeButtonClick">Change values</SfButton> 
    <SfButton OnClick="OnAddButtonClick">Add Value</SfButton> 
</div> 
<div> 
    Id: @id 
</div> 
<div> 
    Collection: @(Newtonsoft.Json.JsonConvert.SerializeObject(records)) 
</div> 
 
@code 
{ 
    SfDropDownList<int?, RecordModel> DDLObject; 
 
    private int? id; 
 
    public class RecordModel 
    { 
        public int? Id { get; set; } 
        public string Text { get; set; } 
    } 
 
    private ObservableCollection<RecordModel> records = new ObservableCollection<RecordModel>() { 
        new RecordModel(){ Id= 1, Text= "A" }, 
        new RecordModel(){ Id= 2, Text= "B" }, 
        new RecordModel(){ Id= 3, Text= "C" }, 
        new RecordModel(){ Id= 4, Text= "D" }, 
        new RecordModel(){ Id= 5, Text= "E" }, 
        new RecordModel(){ Id= 6, Text= "F" }, 
        new RecordModel(){ Id= 7, Text= "G" } 
     }; 
 
    private void OnAddButtonClick(MouseEventArgs args) 
    { 
        records.Add(new RecordModel { Id = records.Max(x => x.Id) + 1, Text = (records.Max(x => x.Id) + 1).ToString() }); 
    } 
 
    private async Task OnChangeButtonClick(MouseEventArgs args) 
    { 
        var list = new ObservableCollection<RecordModel>(records); 
        records = new ObservableCollection<RecordModel>(); 
        foreach (var item in list) 
            records.Add(new RecordModel { Id = item.Id + records.Count, Text = (item.Text + item.Text) }); 
 
        foreach (var item in list) 
            if (!records.Any(x => x.Id == item.Id)) 
                records.Insert(records.Count, item); 
 
        var count = records.Count; 
        for (var i = 0; i < count; i++) 
            if (!list.Any(x => x.Id == records[i].Id)) 
            { 
                records.RemoveAt(i); 
                i--; 
                count--; 
            } 
 
        await InvokeAsync(() => StateHasChanged()); 
    } 
 
} 
 
 
 
Regards, 
Sureshkumar P 



MI Michael September 10, 2020 08:42 PM UTC

Hi Sureshkumar,

Thanks for taking the time to look into this.

As I understand this last code you have sent me, it is basically the same as the answer before, since you assign a new value to the ObservableCollection<> records.
I'm trying to understand why my original code, where I do not assign a new ObservableCollection to the records variable does update in case of the count being different, but not in the case of having the same count.

If the problem has to do with using a List<> instead of an ObservableCollection<>, it should work by just changing the line
var list = new List<RecordModel>();
into 
var list = new ObservableCollection<RecordModel>();

Unfortunately, this has the same behavior as the original sample.

I have changed my code to change the reference of the ObservableCollection<> like you suggested, and it is working correctly, I am just wondering as to why this is necessary, since it is an ObservableCollection<>, which would imply that the value changes are being observed. 

Here is my assumption:
It seems that after the data has changed (note: the data, not the reference), the Count is compared and if it is the same, the data in the UI is not updated.


Best regards,
Mike


SP Sureshkumar P Syncfusion Team September 13, 2020 01:58 PM UTC

Hi Michael, 
 
Thinks for your update. 
 
We would like to say this, the List<> and ObservableCollection<> reference is mismatching so, you have facing the problem. As per our previous update we suggest you change the ObservableCollection instead of List to rectify the issue from your end. 
 
Regards, 
Sureshkumar P 
 



MI Michael September 14, 2020 11:21 AM UTC

Hi Sureshkumar,

Could you please explain to me why the code below is not behaving as would be implied by using an ObservableCollection?
Based on the description found on the MS website (https://docs.microsoft.com/en-us/dotnet/api/system.collections.objectmodel.observablecollection-1?view=netcore-3.1), one would expect that when changing the data of the collection, the control gets notified that there is a change in the data.

----

@page "/DropdownObservableWithSameCount"
@using Syncfusion.Blazor.DropDowns
@using Syncfusion.Blazor.Buttons
@using System.Collections.ObjectModel;

<div>
    <SfDropDownList @ref="DDLObject" TItem="RecordModel" TValue="int" @bind-Value="@id" DataSource="@records">
        <DropDownListFieldSettings Text="@nameof(RecordModel.Text)" Value="@nameof(RecordModel.Id)" />
    </SfDropDownList>

    <SfButton OnClick="OnChangeButtonClick">Change values</SfButton>
    <SfButton OnClick="OnAddButtonClick">Add Value</SfButton>
</div>
<div>
    Id: @id
</div>
<div>
    Collection: @(Newtonsoft.Json.JsonConvert.SerializeObject(records))
</div>

@code
{
    SfDropDownList<int, RecordModel> DDLObject;

    private int id;

    public class RecordModel
    {
        public int Id { get; set; }
        public string Text { get; set; }
    }

    private ObservableCollection<RecordModel> records = new ObservableCollection<RecordModel>() {
        new RecordModel(){ Id= 1, Text= "A" },
        new RecordModel(){ Id= 2, Text= "B" },
        new RecordModel(){ Id= 3, Text= "C" },
        new RecordModel(){ Id= 4, Text= "D" },
        new RecordModel(){ Id= 5, Text= "E" },
        new RecordModel(){ Id= 6, Text= "F" },
        new RecordModel(){ Id= 7, Text= "G" }
     };

    private void OnAddButtonClick(MouseEventArgs args)
    {
        records.Add(new RecordModel { Id = records.Max(x => x.Id) + 1, Text = (records.Max(x => x.Id) + 1).ToString() });
    }

    private async Task OnChangeButtonClick(MouseEventArgs args)
    {
        var list = new ObservableCollection<RecordModel>();
        foreach (var item in records)
            list.Add(new RecordModel { Id = item.Id + records.Count, Text = (item.Text + item.Text) });

        foreach (var item in list)
            if (!records.Any(x => x.Id == item.Id))
                records.Insert(records.Count, item);

        var count = records.Count;
        for (var i = 0; i < count; i++)
            if (!list.Any(x => x.Id == records[i].Id))
            {
                records.RemoveAt(i);
                i--;
                count--;
            }

        await InvokeAsync(() => StateHasChanged());
    }
}




SP Sureshkumar P Syncfusion Team September 17, 2020 09:54 AM UTC

Hi Michael, 
 
Thanks for your update. 
 
As per our previous update on September 10,2020. You have copied the data into variable (List) which does not maintain the reference of the ObservableCollection (records).  So, we suggest you directly add the data into ObservableCollection(records) to resolve the issue. 
 
Please find the code example here: 
private async Task OnChangeButtonClick(MouseEventArgs args)  
    {  
        var list = new ObservableCollection<RecordModel>(records);  
        records = new ObservableCollection<RecordModel>();  
        foreach (var item in list)  
            records.Add(new RecordModel { Id = item.Id + records.Count, Text = (item.Text + item.Text) });  
 
 
 
Regards, 
Sureshkumar P 


Loader.
Up arrow icon