Where to store web custom control data - c#

I am creating a custom control extending WebControl. This web control allows the consumer to define a collection of columns in markup, something like this:
<Custom:CustomGrid>
<Columns>
<Custom:DataColumn HeaderText="FirstName" />
<Custom:DataColumn HeaderText="LastName" />
</Columns>
and put an IEnumerable in a DataSource property and this is rendered out to a table.
This control also allows paging. The IEnumerable in DataSource is the full list, and I display a page of the list at a time. I am already saving the current page, number of rows per page, etc. to viewstate. Should I also put the full list in viewstate? Maybe session?
This list can become a bit hefty. Maybe save in session with a random key, which is saved in viewstate?
What is the best practice here?
Edit: I don't think it's right to impose that all types in the IEnumerable be serializable. Is that fair? So do I need to copy the data source to some other data structure for serialization?
Edit 2: Even if I do use a base control instead of implementing RenderChildControls I will need to implement CreateChildControls, but I will still need to persist the data somewhere, or did I miss the point of the base class?

Indeed, not all IEnumerable instances will be serializable.
If the query is cheap to run I wouldn't persist the whole data set but just run the query again for a different page or a change in the sort order.
If you put the data in viewstate you'll end up with huge pages. Session state might be acceptable if you don't have many users, but large data sets with lots of users won't scale well. What if I bind a million rows to your control? Or what if your control was used in a repeater and shown 100 times on a page?
Are you sure you need to persist the data? This isn't premature optimisation is it?
Remember that your control is a UI component. The viewstate should hold enough information to maintain the UI state as it is. A change in state (e.g.: switching to a different page of results) is something for which your control should pass responsibility to the data source.
Take a look at good old GridView. It displays what you give it and remembers that. If you're using paging then it raises an event to say "the user has changed page; give me page x of data". For me, that's the best practice for a UI control.

For implementing databound control it is better to use base class which was designed to perform such task. For example in ASP.NET exist CompositeDataboundControl which can be used as a base class to implement custom data bound controls. I can advice to review the following Dino Esposito article:
http://msdn.microsoft.com/en-us/library/aa479016.aspx.
Basically if you create control like ASP.NET gridview then it is store values in viewstate. To be more clear it is create number of the DataRow controls which saved assigned values in viewstate. During postback it recreates the same number of rows and values are restored from viewstate. If you will save only datasource for example in session without using viewstate then you will need to redatabind data to your grid during every postback. So, if you create Server control similar to gridview then approach described in the Dino Esposito post will be very helpful because it shows how to create control similar to ASP.NET Server GridView control.

Related

Track ViewState in CompositeDataBoundControl

I created a CustomDataBoundControl. I implemented CreateChildControls(), but I am not actively doing anything to support viewstate and thus, I loose the data after a postback. I was looking in to this article: http://msdn.microsoft.com/en-us/library/aa479016.aspx
My problem is that the way the sample works with ViewState is using a Pair object, which holds key-value pairs and recreates a known data type in the collection of itemsIStateManager` implementation.
My control is more generic. I do not know the type of the objects coming in to the datasource property and I create the child controls via reflection in to the datasource objects. I create a Table control and fill it with TableRow controls, which I fill with TableCell controls.
I can't seem to bridge this gap between Dino`s example and my real-world implementation.
When it comes to data source controls, you don't use ViewState to retain the data. As long as your building a control tree, the .NET framework will reload the viewstate of the controls in the control state. You just need to store the viewstate for the core properties of the control, plus the number of items rendered. This way, you can rerender the same control tree.
create the control tree on init to maintain the ViewState, you need not populate the data in your controls but just create the controls. As per the page flow ViewState is enabled for the controls which are created in OnInit. Hope this helps.

viewstate/databinding for gridview

I have a gridview on my page that gets bound to user search results. There can be many pages upto say 1000. Each page shows 50 records. I have the built in paging turned on for the grid. I want to disable the viewstate on the grid but then I have to bind the results on every page load. (bind twice on paging). The search takes a few seconds and I would not want to store the results in the session. So, how do I achieve turning off the viewstate for the grid or is it okay to have it enabled?
This must be a very common scenario. I hope there is a standard way of doing this.
Depending on how you bind the grid view you should implement server side paging so that your only bringing back the data from the server you need to display for one page.
What data access are you using i.e. are you using linq to sql?
Heres an article on how to do it with ObjectDataSource Custom paging and sorting
Avoid where ever possible putting large amounts of info into view state as it will bloat your page and effect performance.

dynamically generated form how to actually save entered data

i have a form that is generated dynamically.
the plan is to generate it, the user to enter data, and then to save all that lot away.
although a slight variation to this is if the form has previous data associated with it, and then it loads in all pre-populated. - the user may then change any previous selections.
and that is the rub really, i know if i call generateform regardless of postback the viewstate should take over and remember what the settings weer.. but as the generateform method as mentioned above populates the form if the form has previously been saved. which will win, the viewstate or the generateform method for the field populations.. ?
thanks
nat
If you dynamically generate any form controls that post data or cause a postback, you need to recreate them again on postback in order for them to be bound to their data, or their events, after the postback. Conceptually, this makes sense. If you don't have a control in your form after the postback, how could you look at its contents?
There are several ways you could approach this problem.
1) Call GenerateForm() no matter what. Since you said it pre-populates some of the data, you would need to change it so it can be called without doing that. ASP.NET will populate the controls with the data posted automatically on postback, which is what you want.
2) Keep a list of all your dynamically generated controls in a ViewState variable, so you can re-generate them upon postback. For most situations involving dynamically-created controls that aren't very simple (e.g., you may not know in advance exactly what controls are generated), this is the best solution. And often you will want to be able to access the data after a postback, but maybe you really don't want to recreate the whole form because you aren't using it any more.
As long as you recreate a control of the same type and ID on or before Page_Load(), it will be bound to the posted data. It does not need to be in exactly the same place on your form. And it does not need to be used or displayed, either - you can destroy it before the form is rendered, e.g., in Page_PreRender()
3) If you have no interest in any of this, you can always use Request.Form to look directly at the posted data, though this can also be tricky because the names will likely not match your form control IDs exactly. ASP.NET generates unique client-side IDs that depend on the container, and this is what you'll find in Request.Form. If you don't regenerate a control, you may not be able to easily determine the ID that you are looking for. Generally, you should not do this, but it's a way to look at the posted data and sometimes you need it.

How to catch user changes in databound controls?

I have application full of various controls databound to my classes. I would like to ask user "You are closing application and you made some changes. Do you want to save your changes?". For this I need to recognize that user made any changes.
How to catch user made changes in databound controls? Is textBoxXXX_TextChanged the only way to do this?
Thanks in advance for all your answers.
It depends on the datasource; for example DataTable and DataSet sources contain the GetChanges() methods which allow you to easily see if rows have been added/removed/modified. Other data sources will have their own implementations, if any. If there is no implementation then it's up to you to determine how to check for those changes.
In any event this is something you should do at the data-level, not the UI (by watching for "changed" events). Watching events doesn't scale beyond a couple controls and maintenance can be iffy.
Update: Not sure why I didn't think of it, but a second option is to add a BindingSource to your UI object and use it as a databinding proxy (your UI controls databind to the BindingSource and the BindingSource binds to the real datasource). It provides a better approach than handling all your individual "Control_Changed" events, and requiring rework of your other layers (esp. if they aren't custom data-types).
You need to provide custom logic for that, there's not really an automatic way of doing this. As I see it there are several options:
At the start of the editing, save a copy of the original data object, and when you need to check, compare the current object with the saved one. The comparison can be custom (field by field) or semi-automatic by use of serialization (compare the serialized forms) - if it is serializable.
In each of your data object's property set accessors, test for a change in value and mark the object as 'dirty'.
As been discussed, there are many ways to do this depending on how granular you want to get.
A relatively easy way using client side javascript would be to do something like the following:
Hook into the onchange events of the form elements. You could do this dynamically on page load using javascript/DOM.
When the onchange error handler is called, you could set a page level variable: pageHasChanged = true;
Hook into the page's beforeonunload event (occurs when the user tries to navigate away from the page) and check the pageHasChanged variable to see if any changes were made. If changes were made you could alert the user.
This doesn't give you the detail of what changed, but would be fairly easy to modify to track which form elements changed.

DataGrid paging with persistent data

In a Sharepoint web part, I have a DataGrid with paging that I load with all of the data (not using custom paging - custom paging would require a significant overhaul in the current process and is probably one of the last options I can try). I was wondering if it was possible to have it page through the data without re-binding the data source to the grid in the page index changed event? If I remove my current calls to re-bind the data, it remains on the first page no matter what.
With a datagrid, I think you need to re-bind the grid whenever you want to go to a new page.
"A typical handler for the PageIndexChanged event sets the CurrentPageIndex property to the index of the page you want to display and then uses the DataBind method to bind the data to the DataGrid control."
http://msdn.microsoft.com/en-us/library/system.web.ui.webcontrols.datagrid.onpageindexchanged(VS.71).aspx
If you want to avoid querying/fetching the data over again from the source, then you'll need to "cache" the data between postbacks. There are various options here, each with it's own advantages and drawbacks.
If the size of the data is not too large and is not sensitive, you can simply put the data in viewstate on the first page load, and read it out again when the page index is changed. Another option might include using Session to "cache" the data, although this can get tricky if not done right, and of course there will be more load on the server side with this method (with varying amounts, depending on if the Session is In-Proc, State Server, or Database). There may be other methods to "cache" the data, but that is what you'd need to do in this case.

Categories