In version 8.3 this worked beautifully.
I have a webcontrol wherein I'm moving items clientside between two listboxes (as per your own example) and then saving the changes (postback).
In 9.1 the very same code generates an error in an undocumented method. (AreItemsEqualWithoutValues(ListEditItem item1, ListEditItem item2)). Since I have changed nothing in code, I'd like to have a workaround or solution for this problem.
Steps to Reproduce:
Create webcontrol with three listboxes (items,selecteditems,selecteditems2), 8 buttons (add,add2, addall,addall2,remove,remove2,removeall,removeall2)
Datasource can be anything (simpllist will do)
Put control in a popupcontrol and add a savebutton to the footer.
create a label or button to open the popupcontrol.
create a savemethod in codebehind. You should get the error.
Actual Results:
{"Object reference not set to an instance of an object."}
[System.NullReferenceException]: {"Object reference not set to an instance of an object."}
Data: {System.Collections.ListDictionaryInternal}
HelpLink: null
InnerException: null
Message: "Object reference not set to an instance of an object."
Source: "DevExpress.Web.ASPxEditors.v9.1"
StackTrace: " at DevExpress.Web.ASPxEditors.ListBoxProperties.AreItemsEqualWithoutValues(ListEditItem item1, ListEditItem item2)\r\n at DevExpress.Web.ASPxEditors.ListBoxProperties.SynchronizeItems(String serializedItems, Boolean isInsertingSyncType)\r\n at DevExpress.Web.ASPxEditors.ASPxListBox.SynchronizeItems(NameValueCollection postCollection)\r\n at DevExpress.Web.ASPxEditors.ASPxListBox.LoadPostData(NameValueCollection postCollection)\r\n at DevExpress.Web.ASPxClasses.ASPxWebControl.LoadPostDataInternal(NameValueCollection postCollection, Boolean raisePostDataEvent)\r\n at DevExpress.Web.ASPxClasses.ASPxWebControl.System.Web.UI.IPostBackDataHandler.LoadPostData(String postDataKey, NameValueCollection postCollection)\r\n at System.Web.UI.Page.ProcessPostData(NameValueCollection postData, Boolean fBeforeLoad)\r\n at System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)"
TargetSite: {Boolean AreItemsEqualWithoutValues(DevExpress.Web.ASPxEditors.ListEditItem, DevExpress.Web.ASPxEditors.ListEditItem)}
ASPxListBox - Updating the items collection on the client side results in an "Object reference not set" exception if the view state is disabled
Answers approved by DevExpress Support
Hi,
Thank you for the updated sample. First, I should highlight some important things. When changing the ASPxListBox.Items collection at the client side, the control sends the difference between the initial and modified collections to the server. To apply this difference to the initial collection at the server side, it's obviously necessary to load the initial collection first. Once the synchronization has been performed, the newly updated collection should be preserved as the initial collection. This is necessary to restore it during further postbacks. By design, we use the view state as a storage. The view state is disabled in your sample and the initial collection isn't restored. As a result, the difference between the collections sent from the client side is applied to an empty collection. This worked OK in v2008 vol 3. If the changes cannot be applied to the initial collection, v2008 vol 3 simply ignores them. v2009 vol 1 raises an exception in this situation. We've made the necessary corrections to restore the same behavior as in v2008 vol 3. The fix will be included in the next upcoming maintenance update v2009 vol 1.3.
Since you've disabled the view state in your sample, we tend to think that you maintain a separate storage for the Items collection. This way, please note that you must restore the original collection on the Page_Init event handler, not in Page_Load, because the synchronization is performed between these events. Moreover, you should do this on every postback or callback. So, please remove the if(!IsPostBack) verification from your code. Finally, you should save the modified/synchronized collection in your storage. This can be done, for example, in the Page_Load event handler. This approach should work OK both with v2008 vol 3 and v2009 vol 1. Here is some sample code:
C#//protected void Page_Load(object sender, EventArgs e) {
protected void Page_Init(object sender, EventArgs e) {
Dictionary<string, string> Items = new Dictionary<string, string>();
//if(!IsPostBack) {
for(int i = 0; i < 11; i++) {
Items.Add("Item " + i, Guid.NewGuid().ToString());
}
lbOptions.DataSource = Items;
lbOptions.TextField = "Value";
lbOptions.ValueField = "Key";
lbOptions.DataBind();
//}
}
I've also attached some modified files illustrating how to deal with the collections stored in the view state. It works OK in v2009 vol 1. In your sample, the collection isn't stored anywhere. As a result, the project works only until the 2nd postback.
Does this make sense?
Thanks,
Serge
Serge,
Thanks and yes, it does make sense.
Your assumptions were correct. I use a customized ViewState storage object to store objects.
So the nice thing about this is i only have to set EnableViewstate back to true (or delete the whole parameter) and things should work OK.
The reason I did it like is is because I don't want to store what I don't have to.
Since I don't have to store anything about the original collection (I'm moving stuff clientside), I didn't bother with storing the original collection.
So thank you for your reply.
Kind regards,
Arjan Crielaard
Thank you for informing us that the problem is resolved, Arjan. I'm glad that our assistance was helpful.
If you need any further assistance on this subject, please feel free to reactivate this ticket.
Thanks,
Serge
it was slightly incomplete.
javascript in control should be
function MoveItems(lb1, lb2,explicit) {
lb2.BeginUpdate();
var itemCount = lb1.GetItemCount();
for (var i = 0; i < itemCount; i++) {
var item = lb1.GetItem(0);
lb2.AddItem(item.text, item.value);
lb1.RemoveItem(0);
}
lb2.EndUpdate();
UpdateButtons(explicit);
}
function MoveSelectedItem(lb1, lb2,explicit) {
var item = lb1.GetSelectedItem();
if (item != null) {
lb2.AddItem(item.text, item.value);
lb1.RemoveItem(item.index);
}
UpdateButtons(explicit);
}
function SelectOption(explicit) {
if(explicit)
{
MoveSelectedItem(lbOptions, lbExplicitOptions,true);
}
else
{
MoveSelectedItem(lbOptions, lbImplicitOptions,false);
}
}
function SelectAllOptions(explicit) {
if(explicit)
{
MoveItems(lbOptions, lbExplicitOptions,true);
}
else
{
MoveItems(lbOptions, lbImplicitOptions,false);
}
}
function UnselectOption(explicit) {
if(explicit)
{
MoveSelectedItem(lbExplicitOptions, lbOptions,true);
}
else
{
MoveSelectedItem(lbImplicitOptions, lbOptions,false);
}
}
function UnselectAllOptions(explicit) {
if(explicit)
{
MoveItems(lbExplicitOptions, lbOptions,true);
}
else
{
MoveItems(lbImplicitOptions, lbOptions,false);
}
}
function UpdateBoth(){
UpdateButtons(true);
UpdateButtons(false);
}
function UpdateButtons(explicit){
if(explicit)
{
btSelectE.SetEnabled(lbOptions.GetSelectedItem() != null);
btSelectAllE.SetEnabled(lbOptions.GetItemCount() > 0);
btUnselectE.SetEnabled(lbExplicitOptions.GetSelectedItem() != null);
btUnselectAllE.SetEnabled(lbExplicitOptions.GetItemCount() > 0);
}
else
{
btSelectI.SetEnabled(lbOptions.GetSelectedItem() != null);
btSelectAllI.SetEnabled(lbOptions.GetItemCount() > 0);
btUnselectI.SetEnabled(lbImplicitOptions.GetSelectedItem() != null);
btUnselectAllI.SetEnabled(lbImplicitOptions.GetItemCount() > 0);
}
}
Hi,
Thank you for the report. We will research the problem and let you know once we find a solution to it or need an additional information about this bug.
Thanks,
Plato
Hi,
We've created a sample project based upon your description, and it seems to operate as expected. See attached. Perhaps, it's necessary to call these functions in a specific order. Please modify this project to illustrate the problem, if necessary.
Thanks,
Serge
Thanks for the response.
Reproduction wasn't quite as expected.
Enclosed you'll find the usercontrol in question.
You'll notice the difference immediately.
My refences to DevXpress are all done in web.config instead of in the controls and pages.
You might want to change the datasource, since I cannot give you the datasource . It would involve to many libraries.
May I ask you to provide a working sample? We just want to be sure that we're testing under the same conditions … to prevent possible misunderstandings as in our first reply.
Thanks in advance!
Regards,
Serge
Please find attacthed the new usercontrol and a web.config.
Please replace the usercontrol in your example with this control and put and overwrite the web.config