RadListBox limitations?

2 Answers 86 Views
ListBox
Jeff
Top achievements
Rank 2
Iron
Iron
Veteran
Jeff asked on 27 Sep 2021, 12:21 PM

While introducing a screenshot feature to one of our applications, we're experiencing unexpected behavior.  

The screenshots are grabbed as a blob, client-side, converted to a base64 string, then saved in a RadListBox.  We found that if greater than five base64 strings are saved in the RadListBox, the item count is zero, and triggers an error on our server side save functionality.

In most cases the limit of five works as expected, however, at times, even with the limit of five images (as base64 strings), the list box item count is zero.  

Is there a size limit to the RadListBox?  So far, the only explanation I can come up with is the list box control is overwhelmed with data and purges itself.

What else could cause this?  

Jeff
Top achievements
Rank 2
Iron
Iron
Veteran
commented on 27 Sep 2021, 02:52 PM

Upon further inspection, it appears that using the client method to remove the item doesn't actually remove all of the data/values.  This screenshot from dev tools, after removing all items, shows the base64 string value remains.  I've also included a screenshot of the client side remove function.

 

Doncho
Telerik team
commented on 30 Sep 2021, 11:05 AM

Hi Jeff,

I am afraid I was not able to replicate the problem on my side.

Could you please share a sample or code-snippets of the RadListBox declaration and the interactions with its items? We would need to see a repro of the problem in order to help with troubleshooting it.

Jeff
Top achievements
Rank 2
Iron
Iron
Veteran
commented on 01 Oct 2021, 01:48 PM

Doncho,

I have isolated the issue to the removal process (removeItemFromListBox method below).  From the client, the item count is as expected, regardless of how many times the remove function is triggered.  However, once it gets to the server, and if the RadListBox item count/size has reached it's threshold, the item count is zero.  Apparently, the items are purged for some reason, between the client and server.

Attached is an image from the client showing the item count as five, and an image from the server showing 0.  These images are taken immediately following each other, showing that the RadListBox items are purged by the time it gets to the server side code.

Here is the javascript I use to create, and add/remove the base64String representation of the jpeb/blob data:

// Code not shown is methods using the geDisplayMedia functionality which creates a blob from a canvas
// This can be sampled by converting any image/jpeg to a blob, then using the FileReader() function to convert that to a base64 string, as shown below
// Convert blob to base64 string, add to RadListBox
let reader = new FileReader();
reader.readAsDataURL(screenshotJpegBlob);
reader.onloadend = async () => {
   let base64String = reader.result;
   ssCounter++;
   addItemToListBox(base64String);
};



// Add to RadListBox
// "item" = base64String
addItemToListBox = (item) => {
     let lstBox = $find('<%= RadListBox1.ClientID %>');

     let newItem = new Telerik.Web.UI.RadListBoxItem();
     newItem.set_value(item);
     newItem.set_text(ssCounter);

     lstBox.trackChanges();
     lstBox.get_items().add(newItem);
     lstBox.commitChanges();
}

// "item" argument is the text value of each RadListBox item, as set in the addItemToListBox method above
removeItemFromListBox = (item) => {
     let lstBox = $find('<%= RadListBox1.ClientID %>');
     let deleteItem = lstBox.findItemByText(item);

     if (deleteItem) {
         // Delete base64String from RadListBox
         lstBox.trackChanges();
         lstBox.get_items().remove(deleteItem);
         lstBox.commitChanges();
     }

 

 

Jeff
Top achievements
Rank 2
Iron
Iron
Veteran
commented on 01 Oct 2021, 01:52 PM

Additionally, we've determined it's not the number of items, but the size in bytes which causes the problem.  I've narrowed down to approximately 1.5MB being the threshold where this purging occurs.
Jeff
Top achievements
Rank 2
Iron
Iron
Veteran
commented on 01 Oct 2021, 02:43 PM

Is this possibly a buffer overrun? 
Jeff
Top achievements
Rank 2
Iron
Iron
Veteran
commented on 04 Oct 2021, 03:41 PM

Here is the RadListBox declaration:

When deployed, it is a hidden inside a div with style="display: none".  For troubleshooting purposes, the display: none styling is removed.  This problem occurs whether it's hidden or not.


<div>
     <telerik:RadListBox RenderMode="Lightweight" runat="server" ID="RadListBox1" Width="500" Height="500"></telerik:RadListBox>
</div>

2 Answers, 1 is accepted

Sort by
0
Accepted
Doncho
Telerik team
answered on 11 Oct 2021, 04:56 PM

Hi Jeff,

Thank you for the clarification!

Now I was able to completely replicate the described problem on my side.

The unexpected behavior is a result of failing deserialization of the RadListBox's ClientState due to exceeding the maximum length of JSON strings that are accepted by the internally used JavaScriptSerializer object. Currently, the MaxJsonLength property of the serializer is set to its default value of 2097152 characters (=4MB).

What you can do to overcome this limitation:

One possible workaround would be to use a HiddenField to hold and transfer the long value strings to the server. That way you will be able to manually deserialize them with a custom instance of the JavaScriptSerializer. For instance, you can use key-value pairs to store the images in the HiddenField and use just the keys as ListBox values:

<asp:HiddenField id="HiddenField1" runat="server"/>  
JavaScript
var images = {};
var addItemToListBox = (item) => {
    let lstBox = $find('<%= RadListBox1.ClientID %>');
    var hf = $get("<%= HiddenField1.ClientID %>");
    var myId = "myID" + counter;
    images[myId] = item;
    hf.value = JSON.stringify(image);
        
    let newItem = new Telerik.Web.UI.RadListBoxItem();
    newItem.set_value(myId);
    newItem.set_text("ListBoxItem " + counter);

    lstBox.trackChanges();
    lstBox.get_items().add(newItem);
    lstBox.commitChanges();
}

removeItemFromListBox = (item) => {
    let lstBox = $find('<%= RadListBox1.ClientID %>');
    let deleteItem = lstBox.findItemByText(item);
    if (deleteItem) 
    {
        var hf = $get("<%= HiddenField1.ClientID %>");
        delete images[deleteItem.get_value()];
        hf.value = JSON.stringify(images);

        lstBox.trackChanges();
        lstBox.get_items().remove(deleteItem);
        lstBox.commitChanges();
    }
}
C#
var serializer = new JavaScriptSerializer();
serializer.MaxJsonLength = int.MaxValue;
var images =  serializer.Deserialize<Dictionary<string,string>>(HiddenField1.Value);
Note: Have in mind that the Base64 strings may become too long depending on the content of the files and that could cause performance issues when transferring back and forth from server to the client-side. Furthermore, such requests might exceed the max allowed maxRequestLength. If possible, I would recommend decreasing the usage of base64 strings in the requests and consider other options for storing and processing the images.

For your reference here is the relevant piece of the source code:

RadListBox.cs

protected override bool LoadPostData(string postDataKey, NameValueCollection postCollection)
{
	....
	var clientStateValue = postCollection[ClientStateFieldID];
        .....
	var serializer = new JavaScriptSerializer();
	try
	{
		clientState = serializer.Deserialize<RadListBoxClientState>(clientStateValue);
                ....
	}
	catch (InvalidOperationException)
	{
	}
	catch (ArgumentException)
	{
	}
....

I hope you will find this information helpful.

Kind regards,
Doncho
Progress Telerik

Virtual Classroom, the free self-paced technical training that gets you up to speed with Telerik and Kendo UI products quickly just got a fresh new look + new and improved content including a brand new Blazor course! Check it out at https://learn.telerik.com/.

Jeff
Top achievements
Rank 2
Iron
Iron
Veteran
commented on 12 Oct 2021, 04:23 PM

Doncho,

This solution resolved my problem.  Many thanks!

Regarding this line of code:

serializer.MaxJsonLength = int.MaxValue;

Is it necessary to set it to the max value?  We are limiting the accumulative size to 3MB.  Would it be appropriate to set it to, or near, the maxRequestLength property, or even the 3MB threshold?

Thanks,

Jeff.

 

Doncho
Telerik team
commented on 13 Oct 2021, 06:12 AM

Jeff,

I am glad to hear that the problem is resolved.

I have used the int.MaxValue just for demonstrating the suggested approach. Please feel free to set any desired MaxJsonLength that would fit your requirements.

Jeff
Top achievements
Rank 2
Iron
Iron
Veteran
commented on 13 Oct 2021, 02:01 PM

Doncho,

I see now that this value is representing the maximum characters, not size in bytes.  So, I will leave this as is to avoid the same problem.

Thanks.

Jeff.

0
Doncho
Telerik team
answered on 06 Oct 2021, 10:54 AM

Hi Jeff,

Thank you for all the additional information!

It seems that the issue is related to exceeding the maximum allowed request length.

The default value of the maxRequestLength is 4096 KB and if you're operating a large amount of data, its threshold will more likely be exceeded. You can try to increase it to a higher value, for example to 32 MB. 

This can be done by adding the following to the system.web section in your web.config file:
<httpRuntime maxRequestLength="32768" />
For IIS, the property that has to be set is maxAllowedContentLength and its value is in bytes. The following has to be added to the system.webServer section:
<security>
    <requestFiltering>
        <requestLimits maxAllowedContentLength="33554432" />
    </requestFiltering>
</security>
Please give this a try and let me know how it goes.
 

Kind regards,
Doncho
Progress Telerik

Love the Telerik and Kendo UI products and believe more people should try them? Invite a fellow developer to become a Progress customer and each of you can get a $50 Amazon gift voucher.

Jeff
Top achievements
Rank 2
Iron
Iron
Veteran
commented on 06 Oct 2021, 08:05 PM

Doncho,

We do not believe this is related, as our maxRequestLength value is already much higher than the default. 

Additionally, there are no issues with the RadAsyncUpload control used on the same page, with much larger data than 1.5MB.

Based upon what I'm experiencing, it appears that maybe the clientstate isn't being cleared/refreshed when an item is removed from the RadListBox, by the time it gets to the server. 

Were you able to replicate the issue?

Jeff.

Tags
ListBox
Asked by
Jeff
Top achievements
Rank 2
Iron
Iron
Veteran
Answers by
Doncho
Telerik team
Share this question
or