Page 1 of 1

C#: Callback & ViewState

Posted: Sun Mar 01, 2009 1:51 am
by MarauderIIC
I need a solution that allows me to store a user control property in the viewstate after a callback. I tried hacking it like this:

Persister.cs

Code: Select all

    [AttributeUsage(AttributeTargets.Property)]
    public class PersistToViewState : Attribute { }

    public class Persister : UserControl
    {
        protected override void LoadViewState(object savedState)
        {
            if (savedState == null)
                return;

            base.LoadViewState(savedState);
            PropertyInfo[] properties = GetType().GetProperties();
            foreach (PropertyInfo property in properties)
            {
                object[] attributes = property.GetCustomAttributes(typeof(PersistToViewState), true);
                if (attributes.Length > 0)
                {
                    if (ViewState[property.Name] != null)
                        property.SetValue(this, ViewState[property.Name], null);
                }
            }
        }

        //Need to be able to trigger SaveViewState after a callback
        public object SaveViewStatePub() {
            return SaveViewState();
        }

        protected override object SaveViewState()
        {
            PropertyInfo[] properties = GetType().GetProperties();
            foreach (PropertyInfo property in properties)
            {
                object[] attributes = property.GetCustomAttributes(typeof(PersistToViewState), true);
                if (attributes.Length > 0)
                    ViewState[property.Name] = property.GetValue(this, null);
            }
            return base.SaveViewState();
        }
    }
Searchbox_Combobox_Base.cs:

Code: Select all

public abstract class Searchbox_Combobox_Base : Persister { //please pardon the confusing class name.
        public ComboBoxItemCollection selections = new ComboBoxItemCollection();

        [PersistToViewState]
        public ComboBoxItem[] Selections
        {
            get
            {
                ComboBoxItem[] retval = new ComboBoxItem[selections.Count];
                int i = 0;
                foreach (ComboBoxItem item in selections)
                {
                    retval[i] = selections[i];
                    i++;
                }
                return retval;
            }
            set
            {
                foreach (ComboBoxItem item in (ComboBoxItem[])value)
                {
                    selections.Add(item);
                }
            }
        }
AS_SearchDialog.cs:

Code: Select all

    /// <summary>
    /// Grabs the ListItemCollection generated by Search_Multiselect.aspx from Session[id]
    /// and copies it to the selections property of the combobox with the ID of 'id'.
    /// </summary>
    /// <param name="id">ID of the control to search the page for and populate</param>
    private void MakeComboboxMultiselect(string id)
    {
        ListItemCollection items = (ListItemCollection)Session[id];
        if (items == null || items.Count == 0)
            return;

        Control control = Page.FindControl(id);
        if (control is Searchbox_Combobox_Code)
        {
            Searchbox_Combobox_Code box = (Searchbox_Combobox_Code)control;
            box.Text = "Multiselect";
            foreach (ListItem listItem in items)
            {
                ComboBoxItem comboItem = new ComboBoxItem();
                comboItem.Text = listItem.Text;
                comboItem.Value = listItem.Value;
                box.selections.Add(comboItem);
            }
            box.SaveViewStatePub();
        }
        Session[id] = null;
    }

//Updates the combo boxes
    protected void clbUpdatePage_Callback(object sender, CallBackEventArgs e)
    {
        MakeComboboxMultiselect(e.Parameters[0]);
        clbUpdatePage.Content.RenderControl(e.Output);
    }
Alright. So I have a pop-up that has two listboxes, and items are moved between the two listboxes. The 2nd listbox becomes the multiple selection items -- so the user control that contains the combo box also contains a ComboBoxItemCollection named 'selections', you see. And if there has been a multiple selection, then I need to draw the items from this member. The thing is, this isn't automatically saved (perhaps because it's not serializable), so I go to lengths to convert it to an array and save it to the viewstate, and then convert from an array in the viewstate and repopulate the selections member for LoadViewState.

The problem is, that the view state is saved correctly. Then when it goes to be loaded, the view state is no longer populated. So it gets out of sync somehow. IOW if I step through it then when the SaveViewState is triggered, the ViewState takes the information okay. Then when the page is reloaded from the Callback, when it comes to be time to load the ViewState information, the information isn't there any more.
Basic research I've found is http://aspadvice.com/blogs/joteke/archi ... backs.aspx and http://forums.asp.net/p/895595/967316.aspx#967316 neither of which are much help at ~3am.

Also this uses the ComponentArt CallBack control but it shouldn't make any real difference no matter how a Callback is handled.

Anyone know C#/ASP.NET that can help me with this?

Edit: Oh yeah, doing this because otherwise when I hit the execute search button, I lose the information before I can run the search.

Re: C#: Callback & ViewState

Posted: Mon Mar 02, 2009 12:47 am
by trufun202
I'm pretty sure I can help you solve this, but I'm not sure what you're trying to achieve. If it's a User Control that needs to do a Postback, then it's possible that the codebehind isn't getting executed when you think it is, because of the way User Controls are handled in the ASP.NET page life cycle.

You've got 2 listboxes and you're selecting items in Listbox 1 and putting them in Listbox 2, correct? Then you lose me at this part:
so the user control that contains the combo box also contains a ComboBoxItemCollection named 'selections', you see. And if there has been a multiple selection, then I need to draw the items from this member.
It sounds like ultimately you want to be able to store the items in Listbox 2 on the server side so you'll have it when the page is submitted.

To do this, you may want to use AJAX. Which basically allows you to expose a server-side method to the client-side that can be executed via Javascript.

So, assuming you have some sort of "Select" button that's being pressed to move items from Listbox 1 to Listbox 2, you could do something like this:

First, setup an AjaxMethod to save (in Session) the items that appear in Listbox 2.

Code: Select all

//In your aspx.cs codebehind

[AjaxMethod(HttpSessionStateRequirement.ReadWrite)]
public void AddSelectedItem(string selectedItemValue)
{
     List<string> items = (List<string>)Session["items"];
     items.Add(selectedItemValue);
     Session["items"] = items;
}
Then, on your aspx page, add an input button who's onclick method that will send the selected items in Listbox 1 to the server, then move the items to Listbox 2.

Code: Select all

//in your aspx page

<input type='button' value='Select' onclick='MoveSelectedItems()' />

Code: Select all

//Javascript in your aspx page

function MoveSelectedItems()
{
    var listbox1 = document.getElementById("Listbox1");
    var listbox2 = document.GetElementById("Listbox2");

    while (listbox1.options.selectedIndex >= 0 ) 
    {
        //Move the selected item from listbox 1 to listbox 2
        var newOption = new Option();
        newOption.text = listbox1.options[listbox1.options.selectedIndex].text; 
        newOption.value = listbox1.options[listbox1.options.selectedIndex].value; 
        listbox2.options[listbox2.length] = newOption;
        listbox1.remove(listbox1.options.selectedIndex);

       //Make the AJAX call to the server to add this item to the Session state
       Your.Namespace.PageName.AddSelectedItem(newOption.value);
   }
}
Visual Studio 2008 has AJAX.NET built in, but I still prefer AjaxPro (http://www.ajaxpro.info). But, either one should get the job done. (NOTE: The code above only handles Adding SelectedItems from Listbox 1 to Listbox 2 - so you'd need a RemoveSelectedItem if its going back from Listbox 2 to Listbox 1.)

BUT! With all that said, I'm still not exactly sure what you're trying to do...so this may not be at all what you need. :(

If not, can you maybe draw a little wireframe/diagram in MS Paint of what your page is doing?

P.S. All of the code above has no been tested, so mileage may vary. ;)

Re: C#: Callback & ViewState

Posted: Mon Mar 02, 2009 8:42 pm
by sparda
Kinda wish I knew some C# right about now :|

Re: C#: Callback & ViewState

Posted: Mon Mar 02, 2009 8:45 pm
by MarauderIIC
Haven't tested your post (or read much of it, I'm sort of supposed to be doing something else right now) but I noticed the request for clarification, and so the current problem is:
I have a user control with a (ComponentArt) combo box, this control is used multiple times on the same page.
User control contains a custom property, "ComboBoxItemCollection selections"

You can select one item normally in the combo box
Or you can click ++ next to it and have the page with the listboxes pop up
Submitting this page loads the "selections" with the values from the appropriate listbox using a (ComponentArt) CallBack (control). So the page with the listboxes is now gone.

Hitting "Execute search" goes to pull the info from "selections" but "selections" has lost its information. I've attempted to store this in the ViewState (by forcing the saveviewstate call when the callback triggers), but this obviously doesn't work. Watch says its loaded but it's gone when the execute search causes a postback before its event is triggered (which I thought was strange). Probably can't save to ViewState on a callback. The callback contains all the comboboxes and so I'm all kinds of using it wrong.
So in short, my "selections" user control property loses its value even when I attempt to save it in ViewState on callback.

Re: C#: Callback & ViewState

Posted: Mon Mar 02, 2009 9:35 pm
by trufun202
Ugh..I'm not crazy about ComponentArt controls...they're too rigid and force you to do some crazy shit to get them to do want you need.

Is there a specific reason that you're using ComponentArt? It sounds like you can achieve the same functionality using a regular DropDownList and some AJAX. ComponentArt might look a little prettier, but the use of AJAX stills makes for a nice user experience, with no postbacks, and all of your Selections will be saved for you in session when you're ready to postback.

I still don't quite follow how your page needs to function, so I might need an MS Paint drawing or at least some ASCII graphics. ;)

Re: C#: Callback & ViewState

Posted: Tue Mar 03, 2009 10:27 pm
by trufun202
Hey Mar, are you still trying to resolve this issue or have you moved onto other exciting times?

Re: C#: Callback & ViewState

Posted: Tue Mar 03, 2009 11:21 pm
by MarauderIIC
I haven't worked on it in the last couple days.

Re: C#: Callback & ViewState

Posted: Fri Mar 13, 2009 2:36 am
by Aliuar2
Hi there, are you using dynamic controls, and if so, are you creating them oninit?

It's been a little while since I've created a custom web control, but I sympathize with what you're going through.