Listbox - Modify order through Drag&Drop and updated index of object.

Dear Syncfusion team

I'm sturggling for hours with the drag & drop functionality of the listbox.

What i want to achieve:
I have a list of items, where I can add, remove items to the listbox as also reorder them with drag and drop. Further I wan't to be able to edit the count within the ListBox over a numericTextbox. As soon as I click a button i want to get the whole list back with the added / removed items and the Index property accordinling set to my defined order through drag & drop and the count property should contain the value i've modified through the numericTextbox.

    public abstract class MyItems
    {
        public int Id { get; set; }
        public int Index { get; set; }
        public string Name { get; set; }
        public int Count { get; set; }
    }


Thanks in advance :)

9 Replies

SD Saranya Dhayalan Syncfusion Team May 7, 2020 11:46 AM UTC

Hi Sergio, 
 
Thank you for contacting Syncfusion support 
 
Query: I have a list of items, where I can add, remove items to the listbox as also reorder them with drag and drop. Further I wan't to be able to edit the count within the ListBox over a numericTextbox 
 
We have checked your reported query, before that please confirm the below scenario 
 
1.       You have render the numerictextbox component in each list item as a template. When you change the value in numeric textbox that value updated in your count property. 
 
Could you please confirm the above scenario and If we misunderstood your requirement, please provide the above requested information, based on that we will check and provide you a better solution quickly. 
 
 
Query: As soon as I click a button i want to get the whole list back with the added / removed items and the Index property accordinling set to my defined order through drag & drop and the count property should contain the value. 
 
We have checked your reported query you can get the list items added/removed items by using GetDataList method. Also you can update the index value while drag & drop the item in the Dropped event. Please find the below code snippet: 
 
@using Syncfusion.Blazor.DropDowns 
@using Newtonsoft.Json 
 
<div class="col-lg-12 control-section"> 
    <div id="drag-drop-wrapper"> 
        <div class="listbox-control"> 
            <h4>Group A</h4> 
            <SfListBox @ref="listbox1" ID="listbox1" DataSource="@GroupA" Scope="combined-list" TItem="ListData" AllowDragAndDrop="true" Height="330px" TValue="string[]"> 
                <ListBoxEvents TValue="string[]" Dropped="onDropped"></ListBoxEvents> 
                <ListBoxFieldSettings Text="Name"></ListBoxFieldSettings> 
            </SfListBox> 
        </div> 
        
    </div> 
</div> 
@code{ 
 
    SfListBox<string[], ListData> listbox1; 
 
    public object listboxData = new object(); 
 
    public int indexPos; 
 
    public List<ListData> GroupA = new List<ListData> { 
        new ListData { Name = "Australia", Code = "AU", Index = 1 }, 
        new ListData { Name = "Bermuda", Code = "BM", Index = 2  }, 
        new ListData { Name = "Canada", Code = "CA", Index = 3  }, 
        new ListData { Name = "Cameroon", Code = "CM", Index = 4  }, 
        new ListData { Name = "Denmark", Code = "DK", Index = 5  }, 
        new ListData { Name = "France", Code = "FR", Index = 6  }, 
        new ListData { Name = "Finland", Code = "FI", Index = 7  }, 
        new ListData { Name = "Germany", Code = "DE", Index = 8  }, 
        new ListData { Name = "Hong Kong", Code = "HK", Index = 9  } 
    }; 
    
    public class ListData 
    { 
        public string Name { get; set; } 
        public string Code { get; set; } 
        public int Index { get; set; } 
    } 
 
    protected async Task onDropped(Syncfusion.Blazor.DropDowns.DragEventArgs args) 
    { 
        listboxData = await listbox1.GetDataList(); 
        List<ListData> ds = JsonConvert.DeserializeObject<List<ListData>>(JsonConvert.SerializeObject(listboxData)); 
        List<ListData> lists = JsonConvert.DeserializeObject<List<ListData>>(JsonConvert.SerializeObject(args.Items)); 
        indexPos = ds.FindIndex(a => a.Name == lists[0].Name); 
        ListData datasource = GroupA.Find(a => a.Name == lists[0].Name); 
        datasource.Index = indexPos; // you can set the dragged position in this index 
 
 
    } 
} 
 
Please check the above sample and get back to us if you need further assistance on this. 
 
Regards, 
Saranya D 



SR Sergio Ramos May 8, 2020 12:43 PM UTC

Hi Saranya 

Thank you for your response. I've created a sample for you that demonstrates better my use case / scenario.

I've player around a bit so let me shortly explain the new scenario. I have a listbox control where I'm able to add and delete data (as shown in the demo with the buttons) and also can make use of the drag an drop functionality to reorder the things. In every case the index should be updated means propagated to the whole list. Further the displayed list should be sorted defaultly according to the indexes. So far my sample mange contains all of those functionality. But somehow there are bugs as example:
  • Actually I'm not able to add an item to the list with my code it looks that it doesn't work when i use listbox templates.
  • Somehow i broke completly the two-way binding that automatically propagtes the deletion and adding the items from groupA.
I think my code is totally wrong but i hope you can understand what im trying to achieve. 

Regards,
Sergio

Attachment: ListBox_Sample_8f609c2b.zip


SD Saranya Dhayalan Syncfusion Team May 11, 2020 12:26 PM UTC

Hi Sergio,  
 
We have checked your reported scenario, you can add the list items by using the observable collection. We can achieve this in the Dropped event. We can update the index in both dropped event and remove the items. Please find the below code snippet 
 
@using Syncfusion.Blazor.Grids 
@using Syncfusion.Blazor.Buttons 
@using Syncfusion.Blazor.DropDowns 
@using Newtonsoft.Json 
@using Syncfusion.Blazor.Inputs; 
@using System.Collections.ObjectModel 
@using SortOrder = Syncfusion.Blazor.Lists.SortOrder 
@using System.Collections.ObjectModel 
 
<style> 
    .e-listbox-wrapper:not(.e-list-template) .e-list-item, .e-listbox-wrapper .e-selectall-parent { 
        height: initial !important; 
    } 
</style> 
<div class="col-lg-12 control-section"> 
    <div id="drag-drop-wrapper"> 
        <div class="listbox-control"> 
            <h4>Group A</h4> 
 
            <SfButton OnClick="@AddNewCountry" Content="Add New Country"></SfButton> 
            <SfListBox @ref="listbox1" ID="listbox1" SortOrder="SortOrder.Ascending" DataSource="@GroupA" Scope="combined-list" TItem="ListData" AllowDragAndDrop="true" Height="330px" TValue="string[]"> 
                <ListBoxEvents TValue="string[]" Dropped="onDropped"></ListBoxEvents> 
                <ListBoxFieldSettings Text="Index"></ListBoxFieldSettings> 
                <ListBoxTemplates TItem="ListData"> 
                    <ItemTemplate> 
                        <div class="d-flex" style="padding: 0.75rem; border-top: 1px solid #eaeaea;"> 
                            <div class="d-flex align-items-center" style="width: 100px;"> 
                                <span class="btn oi oi-delete " style="cursor: pointer;" @onclick=@(async () => { RemoveItem(context);  })></span> 
                            </div> 
                            <div class="d-flex align-items-center flex-grow-1"> 
                                <div>@((context as ListData).Name)</div> 
                                <div>Index: @((context as ListData).Index)</div> 
                            </div> 
                            <div style="width: 150px;"> 
                                <SfNumericTextBox TValue="int" @bind-Value="@((context as ListData).PeopleCount)" Decimals="0" Min="1" Step="1"></SfNumericTextBox> 
                            </div> 
                        </div> 
                    </ItemTemplate> 
                </ListBoxTemplates> 
            </SfListBox> 
        </div> 
 
    </div> 
</div> 
@code{ 
     SfListBox<string[], ListData> listbox1; 
 
    public ObservableCollection<ListData> GroupA { get; set; } 
 
    public async Task AddNewCountry(MouseEventArgs args) 
    { 
        this.GroupA.Add(new ListData() 
        { 
            Code = "CH", 
            Name = "Switzerland", 
            PeopleCount = 10 
        }); 
 
    } 
 
 public void RemoveItem(ListData listData) 
    { 
        this.GroupA.Remove(listData); 
        int index = listData.Index; 
        this.GroupA.ToList().ForEach((i) => 
        { 
            if (index < i.Index) 
            { i.Index = i.Index - 1; } 
        }); 
    } 
 
    
 
    public class ListData 
    { 
        public string Name { get; set; } 
        public string Code { get; set; } 
        public int PeopleCount { get; set; } 
        public int Index { get; set; } 
        public static ObservableCollection<ListData> getListData() 
        { 
            ObservableCollection<ListData> Data = new ObservableCollection<ListData>(); 
            Data.Add(new ListData { Name = "Canada", Code = "CA", Index = 3, PeopleCount = 8 }); 
            Data.Add(new ListData { Name = "Cameroon", Code = "CM", Index = 4, PeopleCount = 200 }); 
            Data.Add(new ListData { Name = "Denmark", Code = "DK", Index = 5, PeopleCount = 14 }); 
            Data.Add(new ListData { Name = "France", Code = "FR", Index = 6, PeopleCount = 300 }); 
            Data.Add(new ListData { Name = "Australia", Code = "AU", Index = 1, PeopleCount = 3 }); 
            Data.Add(new ListData { Name = "Bermuda", Code = "BM", Index = 2, PeopleCount = 6 }); 
            Data.Add(new ListData { Name = "Finland", Code = "FI", Index = 7, PeopleCount = 466 }); 
            Data.Add(new ListData { Name = "Germany", Code = "DE", Index = 8, PeopleCount = 450 }); 
            Data.Add(new ListData { Name = "Hong Kong", Code = "HK", Index = 9, PeopleCount = 200 }); 
            return Data; 
        } 
 
    } 
 
    protected override void OnInitialized() 
    { 
        this.GroupA = ListData.getListData(); 
 
    } 
 
    protected async Task onDropped(Syncfusion.Blazor.DropDowns.DragEventArgs args) 
    { 
        var listboxData = await listbox1.GetDataList(); 
        ObservableCollection<ListData> ds = JsonConvert.DeserializeObject<ObservableCollection<ListData>>(JsonConvert.SerializeObject(listboxData)); 
        ObservableCollection<ListData> lists = JsonConvert.DeserializeObject<ObservableCollection<ListData>>(JsonConvert.SerializeObject(args.Items)); 
        var indexPos = ds.IndexOf(ds.Where(X => X.Name == lists[0].Name).FirstOrDefault()); 
        ListData datasource = GroupA.Where(i => i.Name == lists[0].Name).First(); 
        datasource.Index = indexPos; // you can set the dragged position in this index 
 
 
    } 
} 
 
For your convenience we have modified a sample. Please find the below sample link 
 
 
Please check the above sample and get back to us if you need further assistance on this. 
 
Regards, 
Saranya D 



SR Sergio Ramos May 13, 2020 08:59 AM UTC

Hi Saranya

Everything seems to work as excepted beside the dragging logic as example. 

Note: I've newly added Sweden with index 0 to the groupA. And added a functionalty that was missing: the new item we can add over the button appears as last item (Index = GroupA.Count() + 1)

So last thing:

Is:
When you drag as exmaple cameroon to the first position cameron receives index 0 but appears as second child in the listbox. Every other items remains the same index.

Before Dragging:

After Dragging:

Should:
When I drag cameroon to the first position cameron receives index 0 and appears as first child and every item get their index updated (reindexed according to the new position in the list)

The list should be displayed and updated within the GroupA as follow:

  • Cameroon Index: 0
  • Sweden Index: 1
  • Australian Index: 2
  • Bermuda Index: 3
  • Canda Index: 4

Thanks in advance for your help :)






SR Sergio Ramos May 19, 2020 05:12 PM UTC

Hi Saranya

Do you have an update on this?

Cheers


SD Saranya Dhayalan Syncfusion Team May 20, 2020 03:48 AM UTC

Hi Sergio,   
 
We have checked your reported query, you can achieve this by using dropped event. In this event you can find the previous index position and current index position. Please find the below code snippet: 
 
protected async Task onDropped(Syncfusion.Blazor.DropDowns.DragEventArgs args) 
    { 
        var listboxData = await listbox1.GetDataList(); 
        ObservableCollection<ListData> ds = JsonConvert.DeserializeObject<ObservableCollection<ListData>>(JsonConvert.SerializeObject(listboxData)); 
        ObservableCollection<ListData> lists = JsonConvert.DeserializeObject<ObservableCollection<ListData>>(JsonConvert.SerializeObject(args.Items)); 
        var prevPos = lists[0].Index - 1; 
        var indexPos = ds.IndexOf(ds.Where(X => X.Name == lists[0].Name).FirstOrDefault()); 
        ListData datasource = GroupA.Where(i => i.Name == lists[0].Name).First(); 
       
        this.GroupA.ToList().ForEach((i) => 
        { 
            if (prevPos > indexPos && i.Index >= indexPos && i.Index <= prevPos && i.Name != lists[0].Name) // dragged from bottom to top 
            { i.Index = i.Index + 1; } 
            else if(prevPos < indexPos && i.Index >= prevPos && i.Index <= indexPos && i.Name != lists[0].Name) // dragged from top to bottom 
            { 
                i.Index = i.Index - 1; 
            } 
        }); 
         datasource.Index = indexPos + 1; // you can set the dragged position in this index 
    } 
 
Please check the above code snippet and get back to us if you need further assistance on this. 
 
Regards, 
Saranya D 



SR Sergio Ramos May 20, 2020 08:50 AM UTC

Hi Saranya

Its coming to an end. I've attached you a sample with a strange bug we still have. Within the zip you can find a video demo. 

Initial State:

Dragged Bermuda up


Dragged bermuda down:


Thanks for helping us in this complex case

Attachment: ListBox_Sample_ad854d5.zip


SD Saranya Dhayalan Syncfusion Team May 22, 2020 02:12 PM UTC

Hi Sergio, 
 
We have validated your reported issue and we are able to reproduce this issue in our end. We have logged a defect report for this, and the fix will be available in our Essential studio 2020 volume 2 release. You can track the status of this defect using below link from our feedback portal,  
 
 
Regards, 
Saranya D 



JC JayaKrishna Ch April 17, 2023 07:41 PM UTC

Hello Sergio,

Make a Observable Collection of ListData Class and give this Collection as the DataSource for the ListBox.
Then in the OnDropped Event
var List_A_Data = listbox1.GetDataList().ToList();
var List_A_DataSource = Data.ToList();
for (int i = 0; i < List_A_Data.Count(); ++i)
{
List_A_Data[i].Index= i + 1;
}
Data = new ObservableCollection(List_A_Data);
Dataa = new ObservableCollection(List_B_Data);
Paste this code.
What this code does is gets the listbox1 items now this list is sorted in the way it is there in the listbox.
So you can simple for loop and get the index using i variable and edit the list to the new index.
Now Convert that list to observable list
Then Bind this observable list to the Datasource.

Loader.
Up arrow icon