views:

3454

answers:

5

I have created an item swapper control consisting in two listboxes and some buttons that allow me to swap items between the two lists. The swapping is done using javascript. I also move items up and down in the list. Basically when I move the items to the list box on the right I store the datakeys of the elements (GUIDs) in a hiddenfield. On postback I simply read the GUIDs from the field. Everything works great but on postback, I get the following exception:

Invalid postback or callback argument. Event validation is enabled using in configuration or <%@ Page EnableEventValidation="true" %> in a page. For security purposes, this feature verifies that arguments to postback or callback events originate from the server control that originally rendered them. If the data is valid and expected, use the ClientScriptManager.RegisterForEventValidation method in order to register the postback or callback data for validation.

I've prepared a test application. All you have to do is download the archive and run the project. On the web page select the 3 items, press Add all, then move the third element up one level and then hit "Button". The error will show up. Turning event validation off is by no means acceptable. Can anyone help me, I've spent two already days without finding a solution.

TEST APPLICATION

A: 

It's complaining because the selected item in a list was not present in the list when it was rendered. Consider using PageMethods via AJAX to get your data back to your form instead of PostBack. Or use a non-input controls to hold the data -- like unordered lists that you move list elements back and forth between. You can put the GUIDs in hidden spans inside the list element where you can get at them if need be.

tvanfosson
However if I simply move elements from the list on the left to the one on the right and then postback without changing their order (up or down) everything is fine. In this case, aren't the two lists different on postback compared to when the page has been written?
iulianchira
More precisely, it's complaining because the selected value in the list wasn't present in the list when the page was written. Adding items is fine, but reordering them leaves an item selected and therefore causes the problem.
stevemegson
Fixed description of problem.
tvanfosson
+2  A: 

The problem is that the saved view state of the list and the data received on postback do not match. The event validation issue is most likely just one of the possible problems that might appear because of this approach. The architecture of webforms does not allow this kind of uses and, most likely, there will be more problems with this approach, even if you succeed to avoid the event validation issue. You have several alternatives:

1) The simplest is to do the swapping logic on server instead of using javascript. This way the view state will be preserved between postbacks and the added overhead of multiple round trips to the server might not be an issue.

2) If the multiple round trips to server is an issue, write a server control that handles it's own view state. This is of course a much engaging approach.

3) A middle ground approach could be to use two simple html lists (just write the html tags without using the asp.net controls) and maintain on the client side from javascript a list of id's in a hidden field. On post back just parse the hidden field and extract the id's ignoring the html lists.

I would go with 1 if there aren't SERIOUS arguments against it.

Aleris
A: 

The first option will bring considerable overhead. I have defined my own custom listbox control derived from the listbox class and performed an override of the loadpostback data:

public class CustomListBox : ListBox
{
    protected override bool LoadPostData(string postDataKey, System.Collections.Specialized.NameValueCollection postCollection)
    {
        return true;
    }
}

Using this instead of the regular listbox in my user control solved the problem, however are there any risks associated with my approach?

iulianchira
You probably want to return false if you do this - returning true will cause SelectedIndexChanged to always fire, but since you've overridden the bit that populates SelectedIndex from the post data, it will never actually change.
stevemegson
+1  A: 

A few possible options:

  • If possible, disable ViewState on the two lists. Without ViewState, the server won't know what the original values were and hence will not error. With this approach, you will need to repopulate the lists (due to lack of ViewState) and may need to track the selection manually - or will need to populate the lists during OnInit phase.

  • Turn off event validation (if you can)

  • Populate both lists fully on the server side and use client side script (javascript) to remove entries from the two lists as required.

Samuel Kim
A: 

By chance, did you try this already? Do this whenever you muck with the list in any way.

document.getElementById("listbox").selectedIndex = -1;
Rob