Inspired from Matt Berseth's awesome Bulk Insert ListView, I present a slightly modified version that is in essence an auto-saving, dynamic row, viewstate-based listview.
It functions basically just like a blank spreadsheet. You start out with one blank row. Need more rows? Click Add. Add too many? Click Delete.
The nice thing is that anytime you click anything, the contents are saved to viewstate via an ObjectDataSource.
After you've entered everything you need, click Submit and do whatever you need to do with the data (send it to a database, etc).
The ListView's ItemTemplate is made up of input controls (TextBoxes, DropDownLists, etc) - so it's basically always in edit mode.. there's just no Edit/Update/Cancel clicking required.
I also demonstrate the use of special-case bindings like the DropDownList that you can't simply %# Bind % like normal TextBoxes.
Saving state is accomplished somewhat similar to Matt's, only mine saves via an overriden OnItemCommand event in an extended ListView class. This makes saving state *almost* completely automatic.
The elegance is in the simplicity. There's nothing quirky or magic about it. It's just a combination of simple concepts that result in an intuitive bulk input grid. And obviously you could also use it to Edit existing data in a dynamic way - just load the ListView on Page_Load.
Again, a huge thanks to Matt Berseth for the original concept/stylings/etc.
Full source is available below:
Hi,
ReplyDeleteI have read ur code. I have couple of doubts
1. Why u have over ridden OnItemCommand
2. Why the Officer must be Serializable.
Plz help me in solving these doubts
Ashwani-
ReplyDelete1) OnItemCommand is overriden so that the listview will essentially auto-save itself whenever there's an insert/delete/etc.
You can still manually cause the listview to save state yourself if you want (you actually have to upon final submit to ensure the last changes were saved).
2) The Officer class must be serializable because it is saved into ViewState (You could also use Session if you wanted).
I'm having some trouble getting it to work with an objectdatasource to a SQLServer table...shouldn't the
ReplyDeletethis.lv.UpdateItem(i.DataItemIndex, false);
abstract the update with the data object layer? Thanks for your help.
Loren-
ReplyDeleteI didn't really intend for it to use a database for maintaining state directly. The database should only be involved at the beginning (if you're loading data for editing) - or at the end (if you're submitting data to be saved). Any on-the-fly editing/adding rows/deleting rows is meant to use viewstate.. kind of like a work-area.. until all changes are made and you are ready to send *everything* to the database.
The way I use it in n-layer design is with the call to the middle layer being done for example (if you're looking at the solution code) in FullEditListView.aspx.cs in the Button1_Click event:
// example submission
myListView1.SaveItems();
Officer o = new Officer(ViewState, VS_OFFICER);
// example only extract non-empty rows
List<Officer> myStuff = o.Extract();
// call middle layer here passing myStuff object or individual parameters, etc to save data to database
BLL.Save(myStuff....)....
And of course the reverse of this holds true meaning that in Page_Load, you could easily call your middle layer to select stored data/build your list of objects, and then just stick it in viewstate so the listview could bind to it (to allow editing of existing data from database).
Thanks-
Hi. How would I add this code to my existing project? I keep getting the error: 'Could not load file or assembly 'ViewStateListView'. Sorry, I'm not familiar with how to actually use this class.
ReplyDeleteAnonymous-
ReplyDeleteIt's not really intended as a reusable control - but rather a design illustration on how to go about building this type of bulk-edit capability into your own app. To make a truly reusable (drop-in) control would be quite a bit of work.
Good luck-
HI Kenneth. I am using this code, it works almost perfectly. The one thing I am having trouble with is bulk delete. I can't seem to be able to delete all items in the list, then re-add new ones. It just seems to not delete existing items, and not add new ones. If you have some 'mass delete' code which correctly deletes the existing contents of the list controls without messing up the objects in memory or view state, please email me: clmhold100@hotmail.com. Thanks very much!
ReplyDeleteSorry to bug you, but there seems to be no way to clear the list. How exactly would I do that? I've spent hours researching this, and nothing works. I can't find the right method to delete. It seems to work only once on the first time I create the object on page load, but then never seems to work after that. But, what if I need to reload from the database on the fly when the user selects a different primary key value, so I have to reload from the database, but I cannot seem to clear all items from the listview programmatically. Thanks.
ReplyDeleteAnonymous-
ReplyDeleteYou can .Clear() the List just as you would any normal List once it's instantiated with the proper ViewState.
If you're using the example posted, drag a new button onto the page, name it btnDeleteAll and wire it to this piece of code:
protected void btnDeleteAll_Click(object sender, EventArgs e)
{
Officer o = new Officer(ViewState, VS_OFFICER);
o.OfficerList.Clear();
o.OfficerList.Add(new Officer("111", "whatever1", "abc"));
o.OfficerList.Add(new Officer("112", "whatever2", "def"));
o.OfficerList.Add(new Officer("113", "whatever3", "xyz"));
myListView1.DataBind();
}
That code will basically .Clear() everything currently in the list (on the page), add 3 new Officers to the List, and rebind the ListView so the changes will be reflected.
Let me know if you have problems-
Ok. Thanks for that code. I need to brush up on my OOP knowledge. As, I dont understand why I would have to instantiate the object every time I want to add or delete a record? I thought that would somehow create a new object and I would not be referencing the right object in memory? I also did that above, except for the myListView1.DataBind(), so I'll try that and let you know how it goes. But, that's exactly what I was trying to do, as every time I want to load a new set of data, I of course have to clear the list and read the new set of records from the database. Thanks!
ReplyDeleteGreat, it works. Thanks very much!
ReplyDeleteMe again: One other question: I want to keep track of which items in the list have been deleted, which have been edited and which have been added. It seems that its easy for edited and added records, as I just go through the list and see if that particular ID is in the database, and if so, just edit it, and if not, just add it. But, for delete, I would probably need a list<> or some other data structure on my page to keep track of all that have been deleted, so I can just go through that 'deleted list' and delete those ID's from the Database when the user clicks the 'save' button. Any idea for code on this? Thanks!
ReplyDeleteAnonymous-
ReplyDeleteThe reason you have to instantiate the object when you want to reference the List is because of how the data is stored in ViewState on the page. When you instantiate it - you pass in the ViewState object on the constructor - which allows it to basically create an in-memory List from the ViewState-based one. There's a ton of different ways to do this kind of thing (i.e. you could use Session and not pass ViewState at all, etc etc). This is just one possible approach.
Hope it helps-
Anonymous-
ReplyDeleteYou should be able to catch the Deleting event and just get the ID of the row being deleted and save it off in another temporary ViewState (or Session) object... then just pull the List out when they click the Save button and do whatever you need to.
Good luck-
Great. Thanks. That's basically what I did for delete. Also, how would I programatically assign the textbox inside the listview a value? I can't seem to find it, when the onselectedindexchanged event fires for that dropdownlist.
ReplyDeleteIt's a regular ListView so you can do a FindControl and get anything inside it just as you normally would.
ReplyDeleteVery useful article. I have one question. How you add an AutoCompleteExtender to one of the Textbox in the ItemTemplate? Any thoughts greatly appreciated.
ReplyDeleteThanks.
I'm sorry, I'm not very familiar with the AutoCompleteExtender. But if it works on a TextBox inside a normal ListView, I don't know why it wouldn't work here?
ReplyDeleteFantastic work,
ReplyDeleteCan you please show me where to stick the code that inserts the records into the db?
If possible, sample would be awesome.
This is a dream find.
Thanks a lot and I pray that I hear from you or some kind soul willing to assist.
--sim
sim-
ReplyDeleteif you'll look at the code for Button1_Click() in FullEditListView.aspx.cs i think it'll be fairly obvious.
the first line:
myListView1.SaveItems();
forces any changes since the last postback to be saved to viewstate.
the next two lines:
Officer o = new Officer(ViewState, VS_OFFICER);
List myStuff = o.Extract();
show basically how to get the data out of viewstate and into a List of objects. once you have them in the List, you can do whatever you want with them.. loop through them, save them to a database, pass them to another class, etc etc.
good luck-