Introduction
When you insert, edit, or delete a feature using the GridView, DetailsView, or FormView control, multiple steps occur when the user adds a new record or updates/deletes a record now. As discussed in the previous section, when you edit a row in the GridView, the Save (Update) and Cancel (cancel) buttons replace the Edit button and the bound column is converted to a TextBox. After the user has updated the data and clicked on the Save button, the following steps are performed at the time of the postback:
1. The GridView control assembles its ObjectDataSource UpdateParameters parameters based on the unique identifying field (through the DataKeyNames property) of the current edit line, along with the value entered by the user
2. The GridView control invokes its ObjectDataSource update () method, which instead invokes the appropriate method of the potential object (Productsdal.updateproduct, in our previous section)
3. Now, these implied data, containing the saved changes, are being rebind to the GridView control
In this series of steps, many events are triggered, which allows us to create an event handler to add custom logic where needed. For example, before step 1th, the event that triggers the GridView. Here, if there is any validation error we can cancel the update request. When the update () method is invoked, the ObjectDataSource updating event is triggered, providing an opportunity to increase or customize the value of UpdateParameters. Triggers the ObjectDataSource updated event after the method of the ObjectDataSource potential object is fully executed. Event handlers for the updated event can check the details of the update operation, such as how many rows of data were affected, or whether an exception was thrown. Finally, after step 2nd, the RowUpdated event of the GridView is triggered, and event handlers for this event can check for additional information about the update operation just completed.
Figure 1 illustrates this series of sequential events and steps when using the GridView update. This event pattern in Figure 1 is not just an update operation in the GridView. This series of pre-level and post-level events occurs when data is inserted, updated, or deleted from the GridView, DetailsView, or FormView, both in the Data Web server control and in the ObjectDataSource.
Figure 1: When data is updated in the GridView, a series of pre-and post-events are triggered
In this section, we will explore the use of these events to extend the built-in insert, update, and delete functionality of the ASP.net data Web server control. We'll also look at how to customize the editing interface to just update some of the product fields.
Step one: Update the ProductName and UnitPrice fields for the product
In the previous section, the editing interface contains all the fields of the product and they are not read-only. If we remove a column (QuantityPerUnit) from the GridView, then the data Web server control will not set the value of the ObjectDataSource QuantityPerUnit UpdateParameters when the update is in place. ObjectDataSource passes a null value to the UPDATEPRODUCT this business logic layer, which changes the QuantityPerUnit field of the currently edited database record to a value. Similarly, if a required field, such as ProductName, is removed from the editing interface, the update will fail and throw a "Column ' ProductName ' does not allow Nulls" exception. This is caused by the fact that ObjectDataSource is configured to invoke the UpdateProduct method of the Productsbll class, which expects each product field to correspond to an input parameter. Therefore, the UpdateParameters collection of ObjectDataSource contains each input parameter of the method.
If we want to provide a data Web server control that allows end users to update only a subset of the fields, then we need to programmatically set the missing UpdateParameters value in the ObjectDataSource updating event handler. Or the BLL method that creates and invokes an expected partial field. Let's explore it in the next steps.
Specifically, let's create a page that displays only the ProductName and UnitPrice fields in an editable gridview. This gridview editing interface will only allow users to update two displayed fields, ProductName and UnitPrice. Since this editing interface only provides a partial field of the product, we need to create a ObjectDataSource that uses the existing BLL UpdateProduct method and programmatically sets the value of the missing field of the product in the updating event handler. Or we need to create a new BLL method that accepts only some of the fields that are already defined in the GridView. In this section, we use the latter to create an overload of the UpdateProduct method that extracts 3 input parameters: ProductName, UnitPrice, and ProductID:
[System.ComponentModel.DataObjectMethodAttribute
(System.ComponentModel.DataObjectMethodType.Update, false)]
public bool UpdateProduct (string productName, decimal unitprice, int productID)
{
Northwind.productsdatatable products = Adapter.getproductbyproductid (ProductID);
if (products. Count = = 0)
//No matching record found and return false to
false;
Northwind.productsrow product = products[0];
Product. ProductName = ProductName;
if (UnitPrice = = null) product. Setunitpricenull ();
else product. UnitPrice = Unitprice.value;
Update the product record
int rowsaffected = adapter.update (product);
Return true if precisely one row is updated, otherwise false return
rowsaffected = = 1;
}
Like the original UpdateProduct method, this overloaded method first checks to see if a product with a specified ProductID exists in the database. If it does not exist, it returns false, indicating that the request to update the product information failed. Otherwise, it updates the ProductName and UnitPrice fields of the existing product record and submits the update by calling the TableAdapter update () method, passing in the Productsrow instance.
With these additional processing of our PRODUCTSBLL class, we can now create a simple GridView interface. Open the datamodificationevents.aspx in the EditInsertDelete folder and add a GridView control to the page. Create a new ObjectDataSource and configure it to use the PRODUCTSBLL class, its select () method maps to GetProducts, and the Update () method maps to only accept ProductName, UpdateProduct method overloads for UnitPrice and ProductID input parameters. Figure 2 shows the Data Source Configuration Wizard when mapping the update () method of ObjectDataSource to the new UpdateProduct method overload of the Productsbll class.
Figure 2: The update () method for mapping ObjectDataSource to the new updateproduct overload
Since our example will simply require the ability to edit data without inserting or deleting records, take the time to explicitly specify the insert () and delete () of the ObjectDataSource. method does not map to any of the methods of the PRODUCTSBLL class-through the tab page to insert and delete and select (none) from the Drop-down list.
Figure 3: On the tab of INSERT and delete, select (none) from the Drop-down list
When this wizard is finished, enable editing is checked from the function tag of the GridView.
After you complete this data Source Configuration wizard and bind to the GridView, Visual Staudio has added their declarative syntax. Go to the source view to see the ObjectDataSource declaration tag, which looks like this:
[System.ComponentModel.DataObjectMethodAttribute
(System.ComponentModel.DataObjectMethodType.Update, false)]
public bool UpdateProduct (string productName, decimal unitprice, int productID)
{
Northwind.productsdatatable products = Adapter.getproductbyproductid (ProductID);
if (products. Count = = 0)
//No matching record found and return false to
false;
Northwind.productsrow product = products[0];
Product. ProductName = ProductName;
if (UnitPrice = = null) product. Setunitpricenull ();
else product. UnitPrice = Unitprice.value;
Update the product record
int rowsaffected = adapter.update (product);
Return true if precisely one row is updated, otherwise false return
rowsaffected = = 1;
}
Because the ObjectDataSource Insert () and Delete () methods do not have mappings, there is no insertparameters or deleteparameters fragment. In addition, because the method maps to the UpdateProduct method overload, it accepts only 3 input parameters, and the UpdateParameters fragment contains only 3 parameter instances.
Note that the ObjectDataSource property is set to Original_{0}. This property is automatically set by Visual Studio when you use the Data Source Configuration Wizard. Therefore, since our BLL method does not need to pass in the original ProductID value, all of these property settings are removed from the ObjectDataSource declaration syntax.
Note: If you simply clear the value of the OldValuesParameterFormatString property from the property window in Design view, this property will still exist in the declaration syntax but will be set to an empty string. Either delete the attribute from the declaration syntax, or set its value to the default value from the property window: {0}.
Although ObjectDataSource contains only the updateparameters,visual of ProductName, UnitPrice and ProductID Studio also adds a bound column or CheckBoxField for each field in the product.
Figure 4: Each field of the corresponding product, the GridView contains a BoundField or CheckBoxField
When the end user edits a product and clicks on its save button (Update), the GridView iterates through the editable fields and assigns the value entered by the user to the corresponding parameter in the ObjectDataSource UpdateParameters collection. If there is no corresponding argument, the GridView adds one to the parameter collection. Therefore, if our GridView contains bound columns or checkbox columns for all the fields of the corresponding product, then ObjectDataSource invokes a method overload that accepts those parameters. But ignoring the ObjectDataSource declaration token specifies the fact that only 3 input parameters are accepted (see Figure 5). Similarly, if there are some combinations that are not read-only columns of the GridView, and none of the UpdateProduct overloads accept the corresponding arguments, an exception is thrown when an attempt is made to save.
Figure 5:gridview adds a parameter to the ObjectDataSource UpdateParameters collection
To ensure that the ObjectDataSource call accepts only the updateproduct overloads of the ProductName, UnitPrice, and ProductID parameters, We need to qualify that the GridView contains only two editable columns, ProductName and UnitPrice. This can be done either by deleting other bound columns and checkbox columns, or by setting the ReadOnly property of these columns to true, or by using both of these methods. In this section, let's simply delete all the columns of the GridView except for the ProductName and UnitPrice bound columns, and then the declaration flags for the GridView are as follows:
<asp:gridview id= "GridView1" runat= "Server" autogeneratecolumns= "False"
datakeynames= "ProductID" Datasourceid= "ObjectDataSource1" >
<Columns>
<asp:commandfield showeditbutton= "True"/>
<asp:boundfield datafield= "ProductName"
headertext= "ProductName" sortexpression= "ProductName"/> <asp:boundfield datafield=
"UnitPrice" headertext= "UnitPrice"
sortexpression= "UnitPrice"/>
</Columns>
</asp:GridView>
Even if the overload expects 3 input parameters, only two bound columns are included in our GridView. This is because the ProductID input parameter is the value of a primary key (primary key) that is passed through the DataKeyNames property of the current edit line.
Our GridView control, along with this updateproduct overload, allows a user to edit only the name and unit price of a product without losing other fields of the product.
Figure 6: Allow editing of ProductName and UnitPrice interfaces only
Perfect UnitPrice format
Although the GridView instance shown in Figure 6 works correctly, the UnitPrice field is completely unformatted, resulting in a missing currency symbol when the price is displayed, and 4 decimal places. To apply currency formatting to a row that is not an edited state, simply set the DataFormatString property of the UnitPrice bound column to {0:C} and its HTMLEncode property to False.
Figure 7: Setting the DataFormatString and HTMLEncode properties of the UnitPrice bound column
With this change, a row in a non edited state is formatted as a currency; however, the currently edited line still appears to have no currency symbol and retains four decimal places.
Figure 8: Now, the row in the non-edit state is formatted as a currency value
By setting the Applyformatineditmode property of this bound column to True (the default is False), you can apply the formatting instructions specified in the DataFormatString property to the editing interface.
Figure 9: The Applyformatineditmode property that sets this bound column is True
With this change, the UnitPrice value displayed in the current edit line is also formatted as currency.
Figure 10: Now the value of the current edit line is also formatted as a currency
However, if you update the product price to the value of the currency symbol in the text box-for example, $19.00– throws a FormatException exception. When the GridView attempts to assign a user-supplied value to the UpdateParameters collection of ObjectDataSource, it cannot convert the UnitPrice string "$19.00" to the decimal type required by the parameter (see Figure 11). To remedy the problem, we can add an event handler for the RowUpdating event of the GridView and have it format the user-entered UnitPrice into decimal in currency format.
The second parameter accepted by the RowUpdating event of the GridView is a Gridviewupdateeventargs type object that contains a newvalues dictionary in which each property holds the value entered by the user. Prepares an assignment to the UpdateParameters collection of ObjectDataSource. We can rewrite the UnitPrice value in the existing NewValues collection to be a monetary amount, resolved by the code in the following event handler:
protected void Gridview1_rowupdating (object sender, Gridviewupdateeventargs e)
{
if (e.newvalues["UnitPrice" ]!= null)
e.newvalues["UnitPrice" =
decimal. Parse (e.newvalues["UnitPrice"). ToString (),
System.Globalization.NumberStyles.Currency);
}
If the user provides a UnitPrice value such as "$19.00", the value is overridden by decimal.parse and resolves to the value of the currency amount. This resolves the currency value correctly, whether it is a currency symbol, a comma, a decimal point, and so on, and uses the NumberStyles enumeration of the System.Globalization namespace.
Figure 11 shows a problem with the currency symbol for the UnitPrice value entered by the user, as well as the use of the GridView RowUpdating event handler to parse such input correctly.
Figure 11: Now, the UnitPrice value of the current edit line is formatted as a currency amount
Step two: block null unitprices
Although the database is configured to allow null values for the UnitPrice fields in the Products table, we may want to prevent users from accessing this particular page and specifying an empty UnitPrice value. More specifically, if a user forgets to enter a UnitPrice value when editing a product line record, instead of saving the result to the database, we might as well give the user a hint that the page, from start to finish, must specify a price for the product editor.
The Gridviewupdateeventargs object of the event handler passed into the GridView contains a Cancel property, which, if set to True, aborts the update process. Let's extend the RowUpdating event handler and set E. Cancel is true and displays a message explaining why the UnitPrice value in the NewValues collection is null.
First, add a label server control to the page and name it mustprovideunitpricemessage. This label control will show whether the user forgot to specify a UnitPrice value when updating a product. Set the Text property of this label to "you must provide a price for the product." ”。 I have also added a new CSS category named warning in the Styles.css file, which is defined as follows:
. Warning
{
color:red;
Font-style:italic;
Font-weight:bold;
Font-size:x-large;
}
Finally, the CssClass property of the label is set to warning. This will display a red, italic, bold, and large font warning message above the GridView in the designer.
Figure 12: A Label control has been added above the GridView
By default, this label control is hidden, so setting its Visible property in the Page_Load event handler is false.
protected void Page_Load (object sender, EventArgs e)
{
mustprovideunitpricemessage.visible = false;
}
If a user tries to update a product without specifying a UnitPrice value, we want to cancel the update and display a warning label. Add the following code to the RowUpdating event handler in the GridView:
protected void Gridview1_rowupdating (object sender, Gridviewupdateeventargs e)
{
if (e.newvalues["UnitPrice" ]!= null)
{
e.newvalues["UnitPrice"] =
decimal. Parse (e.newvalues["UnitPrice"). ToString (),
System.Globalization.NumberStyles.Currency);
}
else
{
//show the Label
mustprovideunitpricemessage.visible = true;
Cancel the update
e.cancel = true;
}
If a user tries to save a product and does not specify a price, the update operation is canceled and a useful hint message is displayed. Although the database (and business logic) allows the UnitPrice of null values, this particular asp.net page is not allowed.
Figure 13: The user cannot leave UnitPrice blank
So far we've seen how to programmatically change the parameter values of the UpdateParameters set assigned to ObjectDataSource by using the RowUpdating event of the GridView, and how to completely cancel the update process. These ideas can also be extended to DetailsView and FormView controls and applied to insertions or deletions.
These tasks can also be completed by inserting, updating, and deleting event handlers that are passed at the ObjectDataSource level. These events are triggered before the associated method of the suppressed object is invoked, and provide a last chance to change the input parameter collection or completely cancel the operation. The event handlers that correspond to these 3 events pass in an object of ObjectDataSourceMethodEventArgs type, and we are interested in both of its properties:
· Cancel, if it is set to true, cancels the operation in execution
· InputParameters, which is a collection of insertparameters, UpdateParameters, or deleteparameters, depends on the inserting, updating, Or the event handler for the deleting event
To illustrate how to handle the value of a parameter in the ObjectDataSource layer, let's include a deailsview in the page that allows the user to add a new product record. This DetailsView will be used to provide a quick way to add a new product record to the database. To keep the interface consistent for new products, we only allow users to enter ProductName and UnitPrice fields. As a default, the values provided in the DetailsView insert interface are set to a null value for a database. However, we can use the ObjectDataSource inserting event to inject different defaults, as we will soon see.
Step three: Provide an interface to add new products
At the top of the GridView, drag a DetailsView control from the toolbox to the designer, empty its height and width properties, and bind it to the ObjectDataSource that already exists on the page. This adds a bound column or CheckBox column for each field in the product. Because we want to use this DetailsView control to add a new product, we need to check to enable inserting this entry from its functional tag; however, there is no such item because this ObjectDataSource insert () Method has not yet mapped to the Productsbll class method (recall that we set this mapping to (none) when configuring the data source-see Figure 3).
To configure this ObjectDataSource again, download the wizard by selecting the "Configure Data source" link from its function tag. The first screen allows you to change the implied object to which ObjectDataSource is bound; let it remain productsbll. The next screen lists the mappings of methods from ObjectDataSource to suppressed objects. Although we have explicitly specified that insert () and delete () do not map to any method, however, if you go to the tab page of INSERT and delete you will still see a map there. This is because the addproduct and Deleteproduct methods of PRODUCTSBLL use Dataobjectmethodattribute to indicate that they are the default method for the insert () and delete () services respectively. Therefore, the ObjectDataSource Wizard automatically selects them each time it runs, unless there is explicitly a different value specified.
Let the insert () method still point to the Addproduct method, but again select (none) from the Drop-down list in the tab of the delete.
Figure 14: Selecting the Addproduct method from the Drop-down list of the Insert tab
Figure 15: Select (none) from the Drop-down list in the tab of the delete
When these changes are complete, the ObjectDataSource Declaration syntax will contain a InsertParameters collection, as follows:
<asp:objectdatasource id= "ObjectDataSource1" runat= "Server" selectmethod= "getproducts" typename= "PRODUCTSBLL" Updatemethod= "UpdateProduct" onupdating= "objectdatasource1_updating" insertmethod= "AddProduct" oldvaluesparameterformatstring= "original_{0}" > <UpdateParameters> <asp:parameter name= "ProductName" Type= "String"/> <asp:parameter name= "UnitPrice" type= "Decimal"/> <asp:parameter the Name= "ProductID" Typ E= "Int32"/> </UpdateParameters> <InsertParameters> <asp:parameter name= "ProductName" type= "Stri" ng "/> <asp:parameter name=" SupplierID "type=" Int32 "/> <asp:parameter name=" CategoryID "Type=" Int32 " /> <asp:parameter name= "QuantityPerUnit" type= "String"/> <asp:parameter name= "UnitPrice" Type= "Decima" L "/> <asp:parameter name=" UnitsInStock "type=" Int16 "/> <asp:parameter name=" UnitsOnOrder "Type=" Int1 6 "/> <asp:parameter name=" ReorderLevel "type="Int16 "/> <asp:parameter name= discontinued" type= "Boolean"/> </InsertParameters> </asp:objectdat
Asource>
Running the wizard again causes the OldValuesParameterFormatString property to be added again. Set this property to the default value ({0}) or remove them completely from the declaration syntax.
As ObjectDataSource provides the ability to insert data, the DetailsView function tag now contains the checkbox "Enable Insert"; go back to the designer and tick the item. Then, reduce the DetailsView column straight it contains only two bound columns-ProductName and UnitPrice, and a CommandField. The declaration syntax for this DetailsView will look like the following:
<asp:detailsview id= "DetailsView1" runat= "Server" autogeneraterows= "False"
datakeynames= "ProductID" Datasourceid= "ObjectDataSource1" >
<Fields>
<asp:boundfield datafield= "ProductName"
headertext= "ProductName" sortexpression= "ProductName"/> <asp:boundfield datafield=
"UnitPrice" headertext= "UnitPrice"
sortexpression= "UnitPrice"/> <asp:commandfield
"True"/ >
</Fields>
</asp:DetailsView>
Figure 16 shows a page that is viewed through the browser at this time. As you can see, DetailsView lists the name and price of the first product (Chai). However, what we need is an insertion interface to provide a means for users to quickly add a new product to the database.
Figure 16: The DetailsView is currently present in read-only mode
To show the DetailsView of the insert mode we need to set the DefaultMode property to inserting. This allows DetailsView to present the insertion mode after the first access and insertion of a new record. As shown in Figure 17, such a DetailsView provides a quick interface for adding new records.
Figure 17: This DetailsView provides a quick interface to add new products
When the user enters a product name and price (such as "Acme wate" and 1.99, as shown in Figure 17) and clicks on the Insert button, a postback occurs and the workflow begins inserting until the final add a new product record to the database. The DetailsView is maintained at its insertion interface and the GridView automatically reproduces the data source bound to it, in order to contain the newly added product, as shown in Figure 18.
Figure 18: Product "Acme Water" has been added to the database
Although not shown in the GridView in Figure 18, the missing product fields in the DetailsView interface –categoryid, SupplierID, QuantityPerUnit, and so on-are assigned a database null value. You can see this by performing one of the following steps:
1. Server Explorer to Visual Studio
2. Expand Northwnd.mdf Database Node
3. In the products datasheet power-saving on the right click
4. Select "Show Table Data"
This will list all the records in the Products table. As shown in Figure 19, the other fields of our new product are null values except for the ProductID, ProductName, and UnitPrice fields.
Figure 19: Other fields for products not provided in DetailsView are assigned null values
We might want to give one or several of these fields values a default value, not NULL, either because NULL is not the best default item, or because the database field itself does not allow null values. To do this, we can programmatically set the parameter values in the InputParameters set of these DetailsView. This work can be done in a DetailsView Iteminserting event handler, or in a ObjectDataSource inserting event handler. Because we've seen how to use the pre-and post-level events in a data Web server control, let's explore the ObjectDataSource event this time.
Step Fourth: Assign values to CategoryID and SupplierID parameters
Here we assume that when our application adds a new product through this interface, it should assign a value of 1 to the CategoryID and SupplierID fields. As mentioned earlier, the ObjectDataSource control has a pair of pre-and post-level events that occur during the data change process. When its insert () method is invoked, ObjectDataSource first triggers its inserting event, and then calls the business method that its insert () method maps to, and finally triggers the inserted event. This inserting event handler provides us with a last chance to process input parameters or completely cancel the operation.
Note: In a real-world application you might want to let users specify both category and supplier values, and you want to choose this value based on certain standards and business logic (rather than blindly selecting ID 1). In any case, this example illustrates how to programmatically set the value of an input parameter in a ObjectDataSource Pre-level event.
Take the time to create an event handler for the ObjectDataSource inserting event. Note that the second input parameter of the event handler is an object of type ObjectDataSourceMethodEventArgs, which has a property to access the parameter collection (InputParameters) and a property to cancel the operation (cancel).
protected void objectdatasource1_inserting
(object sender, ObjectDataSourceMethodEventArgs e)
{
}
At this point, the InputParameters property contains a InsertParameters collection that is assigned to the ObjectDataSource by DetailsView. To modify a value in these parameters, simply use: E. inputparameters["paramname"] = value. So, to set the CategoryID and SupplierID to 1, adjust the inserting event handler as follows:
protected void objectdatasource1_inserting
(object sender, ObjectDataSourceMethodEventArgs e)
{
E. Inputparameters["CategoryID"] = 1;
e.inputparameters["SupplierID"] = 1;
}
This time when we add a new product (such as Acme Soda), the CategoryID and SupplierID fields for this new product are assigned 1 (see Figure 20).
Figure 20: Now, the CategoryID and SupplierID fields for the new product are set to 1
Summarize
Many pre-and post-level events occur in the process of editing, inserting, and deleting, whether the data Web server control or the ObjectDataSource. In this section, we looked at pre-level events and saw how to use them to customize input parameters or cancel current data change operations in data Web server controls and ObjectDataSource events. In the next section, we'll look at the event handlers that create and use post-level events.
I wish you a happy programming!
Introduction of the author
Scott Mitchell, with six asp/asp. NET book, is the founder of 4GuysFromRolla.com, has been applying Microsoft Web technology since 1998. Scott is an independent technical consultant, trainer, writer, recently completed a new book to be published by Sams Press, proficient in asp.net 2.0 within 24 hours. His contact email is mitchell@4guysfromrolla.com, or he can contact him through his blog Http://ScottOnWriting.NET.