+1  A: 

Since no one else has taken a stab at this for 2 hours, I'll throw my hat in the ring with a solution that does not rely on viewstate at all (or the ASP.NET model of postbacks).

What if you grabbed all the input values with jQuery and instead of doing a post-back did a post against the page (or a new results.aspx page)? Or, you could make the entire thing asyncrhonous and do an Ajax request against a web method, get fed the results, and populate on the client side as needed?

The unfortunate thing here is you have to reconstruct which type of controls were used to figure construct your search query since that data wont be passed with the viewstate. But I imagine you were already going to have to do some kind of translation of your input data into a query form anyway.

Read here for more information about using jQuery to hit an ASP.NET page method. Remember - page methods must be static (it's an easy oversight).

I'm not sure what you're doing server side to construct your query - but I would highly recommend LINQ. I did a similar "advanced search" function previously, and after a few different attempts found that LINQ was a wonderful tool for this problem, regardless of whether I was hitting SQL with LINQtoSQL or just hitting an in-memory collection of objects.

This worked so well because 1) LINQ is deferred execution and 2) A LINQ query returns another queryable object. The implication here is that you can chain your LINQ queries together as you construct them from your input, instead of having to do a single massive clause translation to SQL or whatever backstore you are using (one of my attempts was constructing SQL clauses with strings, but still passing input data via SQLParameters for SQL injection protection - it was messy and complicated when hand crafted LINQ was orders of magnitude easier to understand and implement).

For example:

List<string> data; // or perhaps your a DB Context for LINQtoSQL?

var query = data.Where(item => item.contains("foo"));

if( {user supplies length search option} )
    query = query.Where(item => item.Length < 5);

// etc, etc.

// LINQ doesn't do anything until the query is iterated, at which point
// it will construct the SQL statement without you worrying about details or parameter binding
foreach(string value in query)
    ; // do something with the results

Because of deferred execution and the queryable return type, you can concatenate LINQ queries to this expression all day long and let it worry about the implementation details (such as converting to a SQL query) at execution time.

Matt
Commenting on my own answer here because this is not relevant to the question/answer, and is highly opinionated - but this is one of the reasons I dropped ASP.NET in favor of ASP.NET MVC. The HTTP protocol is not stateful, and the ASP.NET model of viewstate is really just a monkey-patch to get state on a web application. And as such, you encounter these sorts of problems. While there might be a true ASP.NET/postback solution, the complexity involved begins to outweigh the benefits. After all, you're just trying to send data back to the server! Should be easy as pie.
Matt
Thanks for taking the time to explain all this Matt. While it does dodge my specific problem with ViewState, it does so by discarding the whole approach and introducing a number of new challenges. This is a bit of an "everything looks like a nail when all you have is a hammer" solution - something I see on SO a lot with jQuery for some reason.
Barry Fandango
Thanks also for the advice about the API and DB side, I know it's not related to the question but I appreciate hearing another point of view on the subject.
Barry Fandango
@barry, understandable. That's why I held off until I noticed after 2 hours you still didn't have any answers. Obviously, there are multiple ways to accomplish the same goal, some better than others, and that varies by person even. As for jQuery - its popoluar in many communities online, but particularly so in .NET because Microsoft is in fact shipping it as part of VS2008. You'll notice if you create a new web project (at least I did in ASP.NET MVC) that the javascript folder has jQuery in it already.
Matt
I know we're getting off-topic Matt, but that's pretty awesome eh? For MS to pluck a best-of-breed JS framework from the wild regardless of its licensing and incorporate it into something like the MVC product is really amazing to see.
Barry Fandango
+1  A: 

I can't provide you with the exact steps that you will need to do, but I HIGHLY suggest looking into asp.net page life cycle. I created a user control as a DLL one time. I had to capture postback data at specific steps in the lifecycle and recreate and rebind the data at other steps. Additionally thinkgs like viewstate are only available at certain points also. I know that I had to override On_init, On_prerender and some other methods.

Sorry I couldn't be more help, but I don't have the code with me (its with an old employer). I hope this helps.

andrewWinn
A: 

If you are adding controls to the controls tree dynamically, you need to add them on postpack as well. Just call the method that builds the control on Page_Load or Page_Init and the controls should stay on the page on postback.

Ender
+1  A: 

I've been coding for about a day and I got this working beautifully using the third option I suggested in my question - old-school databound controls. Actually I only thought of the idea when I was forced to write out the question in detail - doesn't that just happen to you all the time?

I put my SearchCriterionControl into an asp:Repeater and bound it to my object collection. For the Field Chooser I put an asp:DropDownList inside a nested asp:Repeater and bound the Field array to that. Everything works beautifully, keeps state, actually required very little code. So I never had to dynamically add controls to the page, thank goodness.

Thanks for your suggestions, Ender, Matt and andrewWinn.

Barry Fandango