2

I have two select lists in my ASP.NET site that are filled by the server with some elements.

// .aspx
<asp:dropdownlist id="abc" runat="server"></asp:dropdownlist>
<asp:dropdownlist id="def" runat="server"></asp:dropdownlist>

// .aspx.cs
abc.Items.Add(new ListItem("element1", "value1"));
def.Items.Add(new ListItem("element1", "value1"));

Due to too complicated reasons to explain right now, I also need to modify the options of the select lists with JavaScript, adding some values.

// In the <head> of the .aspx page
var abcList = document.getElementById("abc");
var defList = document.getElementById("def");
var newAbcElement = new Option("element2", "value2", false, false);
var newDefElement = new Option("element2", "value2", false, false);
abcList.options[abcList.length] = newAbcElement;
defList.options[defList.length] = newDefElement;

Of course, this will mess up Even Validation as soon as I send the form back to the server (be it by submitting or as a PostBack from some other form elements with AutoPostBack="true").

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.

Now, I don't have the resources and budget to completely overhaul the whole page design, so: What is the fastest and easiest way to change the dropdownlist that does not mean I have to rewrite the whole thing?

So that the values added via JavaScript are recognized by my CodeBehind file when submitting the form?

F.P
  • 17,421
  • 34
  • 123
  • 189
  • 1
    Do you need the ViewState to include this control, because you can just disable the ViewState for it. – Konstantin Dinev Feb 26 '13 at 09:50
  • Have you considered using an AsyncPostBackTrigger to pass those new items to the server-side asynchronously and add them to the appropriate list there? That way, it will pass Event Validation. – mostruash Mar 04 '13 at 22:53
  • Yes but I never found any usable, working minimum examples of how to implement this on the ASP, C# **and** JavaScript side. – F.P Mar 05 '13 at 07:23

3 Answers3

1

You can disable the ViewState entirely for the DropDownList.

<asp:dropdownlist id="abc" runat="server" EnableViewState="false"></asp:dropdownlist>

To your question update:

This changes the question quite a lot. The new question is answered here: Invalid postback or callback argument. Event validation is enabled using '<pages enableEventValidation="true"/>'

Community
  • 1
  • 1
Konstantin Dinev
  • 34,219
  • 14
  • 75
  • 100
  • That didn't quite work. I think I also had the question wrong, because the issue is with the "Event validation" and not the ViewState. I will update my question, sorry for the confusion. – F.P Feb 27 '13 at 08:02
  • I have read that question already. I wanted to go with the *Use ASP.NET Ajax UpdatePanel* but couldn't find any examples of how to actuall do that in correspondance to my code. – F.P Feb 27 '13 at 08:08
  • @FlorianPeschka try replacing this line abcList.options[abcList.length] = newElement; with abcList.options.add(newElement;) – Konstantin Dinev Feb 27 '13 at 08:16
  • Doesn't change the error message :-/ Also; how would I need to implenent the UpdatePanel - When I just wrap it around the dropdownlist, it doesn't change anything. – F.P Feb 27 '13 at 08:28
  • @FlorianPeschka Those are the suggestions I can help you with, in order to implement the code for the update panel I would have to go through the same process as you would :( – Konstantin Dinev Feb 28 '13 at 12:48
1

You have a few options.

First, you might rewrite your code so that the server side generates all possible items for the DropDownList and then in your JavaScript remove the unneeded items instead of adding new ones.

Second option is to create a custom class derived from System.Web.UI.WebControls.DropDownList. The class should contain the one method shown below. The important piece is that your custom class will not have the System.Web.UI.SupportsEventValidationAttribute added to it - so DropDownList base methods will skip the event validation automatically. Now replace the usage from <asp:dropdownlist> to your method.

In case you can't modify the .aspx code (or you have a ton of dropdownlists to replace) you might use tag mapping in your configuration.

namespace Project.MyWebControls
{
    public class MyDropDownList : System.Web.UI.WebControls.DropDownList
    { 
        protected override bool LoadPostData(string postDataKey, NameValueCollection postCollection)
        {
            if (base.LoadPostData(postDataKey, postCollection))
                return true;

            // this means that the value selected was not present in the .Items collection
            string[] values = postCollection.GetValues(postDataKey);
            if (values == null || values.Length == 0)
                return false;

            // add the value to the Items collection so that it can be processed later on.
            this.Items.Add(new ListItem("Custom value created by JavaScript", values[0]));
            this.SetPostDataSelection(this.Items.Count - 1);
        }
    }
}

Note that depending on your code you might want to remove these custom values from the Items collection before rendering.

Sample for the .aspx file:

<%@ Register TagPrefix="my" Namespace="Project.MyWebControls" Assembly="Project" %>
<my:MyDropDownList runat="server" ...></my:MyDropDownList>
Knaģis
  • 20,827
  • 7
  • 66
  • 80
  • Thanks for the ideas. The first suggestion is not possible - the values are dynamically created from the client browser, so there's not really anything to go on regarding pre-generation. The second one sounds interesting, though. But: Will the values that I inject via JavaScript be recognized on the ServerSide on PostBack? So that I can handle them in my CodeBehind, just like "original" values? – F.P Feb 28 '13 at 12:37
  • Added an override for LoadPostData so that you can use the value from JavaScript on the server like any other value. – Knaģis Feb 28 '13 at 12:50
  • How would I need to change the ``-Element? Currently, I get an "type is not compatible with the type of control" exception when viewing the page. – F.P Feb 28 '13 at 13:15
  • that exception would be shown if you did not derive from the correct class. I extended the code sample with additional examples. – Knaģis Feb 28 '13 at 13:30
  • We're getting there. But somehow I can't access the value selected item from that custom dropdown class. I always is = 0, even if I select one of the items added via JavaScript. – F.P Mar 01 '13 at 10:12
1

Ok, here is one more option for you. You can add those items to your lists using AsyncPostBackTrigger.

Some hidden fields:

<asp:TextBox ID="newItemsForAbc" runat="server" style="display:none;"></asp:TextBox>
<asp:TextBox ID="newItemsForDef" runat="server" style="display:none;"></asp:TextBox>
<asp:Button ID="addNewItems" runat="server" OnClick="addNewItems_Click"
  style="display:none;" />

The Update Panel:

<asp:UpdatePanel runat="server" ID="UpdatePanel" UpdateMode="Conditional">
   <ContentTemplate>
     <asp:dropdownlist id="abc" runat="server"></asp:dropdownlist>
     <asp:dropdownlist id="def" runat="server"></asp:dropdownlist>
   </ContentTemplate>
   <Triggers>
     <asp:AsyncPostBackTrigger ControlID="addNewItems" EventName="Click" />
   </Triggers>
 </asp:UpdatePanel>

JS Function for doing an async post back:

<script type="text/javascript"> function UpdateStuff(value1, value2)
{
   var abcItems = document.getElementById("<%= newItemsForAbc.ClientID %>");
   var defItems = document.getElementById("<%= newItemsForDef.ClientID %>");
   abcItems.value=value1;
   defItems.value=value2;
   __doPostBack("<%= addNewItems.ClientID %>","");
}
</script>

Server-side function that handles button click:

protected void addNewItems_Click(object sender, EventArgs e)
{
    string[] n1 = newItemsForAbc.Text.Split(';');
    string[] n2 = newItemsForDef.Text.Split(';');

    foreach(string i in n1)
    {
         abc.Items.Add(new ListItem(i, i));
    }

    foreach(string i in n2)
    {
         def.Items.Add(new ListItem(i,i));
    } 
}

To Update Your Lists:

var newAbcElements = "Element1;Element2;Element3";
var newDefElements = "Element4;Element5";
UpdateStuff(newAbcElements, newDefElements);

Final note: This piece of code probably will not do the job for you as it is. You may need to change the way you store new items in a string, thus splitting/parsing would change too. You may even need different strings for displaying a list item and its actual value. But I believe you get the basic idea.

mostruash
  • 4,169
  • 1
  • 23
  • 40
  • This is great. Thanks for all the work. It pointed me exactly in the right direction. – F.P Mar 05 '13 at 15:22