Getting Under the DetailsView Control

Programmatically Manipulate Data-bound Controls and Discover the ASP.NET Pipeline Along the Way

Greetings ASP.NET architects and developers! This month I m going to answer some frequently asked questions about working with data-bound controls. For more information on the DetailsView control, see "Enhancing the ASP.NET DetailsView Control ."

Q. I want to do an Insert/Cancel operation using a DetailsView control on a Web page. I set the default mode to Insert and have been able to insert records to the database. What I would like to do is persist some of the data between inserts so I don t have to retype all the data each time. However, each insert clears the DetailsView form and I can t find where or how to make the data persist. Is this possible?

A. ASP.NET 2.0 and Visual Studio 2005 help developers to rapidly develop and deploy Web applications. For example, I can create in a matter of hours a fully functioning Web application complete with master page templates, rich navigation, security features such as membership and role-based security, and page content that presents content from a back-end data store.

As you might expect, for all the productivity provided by the built-in features of ASP.NET 2.0, there are trade-offs. For example, using data controls such as the GridView, DetailsView, and various data source controls provides a productive wizard-driven experience to connect page content to real data. For this convenience, the price you pay is in flexibility. To customize these controls you must roll up your sleeves and get your hands dirty by hooking events and writing code. In this article I ll show you how to get under the hood of the data-bound controls, and show you some practical ASP.NET tricks that you can apply in your Web applications.

What We'll Cover

There are many ways to accomplish the objectives set forth in the reader s question. The answer, it turns out, addresses a few nuggets that can be useful in other areas of your Web applications. Thus, during the course of this article, I'll cover:

  • The DetailsView control
  • Using template fields
  • Comparing the Eval and Bind methods
  • Using FindControl
  • The ASP.NET Context object and its Item collection

Connecting to Data: The Drag and Drop Way

Before I address the reader s question, let me walk you through a few steps to set up a Web application that consumes data using the SqlDataSource control and a GridView control. To consume data on a Web page, you may start by dragging a GridView control on the page in design view. If you create a new data source from the GridView Tasks pane the Data Source Configuration Wizard will be presented. From here you can select Database as the data source type and provide a new connection string (using the famous Northwind database for this example) and a new SqlDataSource will be added to the page and associated with the GridView control.

For this example, let s assume you selected a subset of the Customer table for the data source control. By default, the SqlDataSource control s SelectCommand property will be initialized. The following declaration illustrates one possible SELECT statement:



 

You can also use the SqlDataSource control for inserts by setting the InsertCommand property:



 

This can be entered directly in the control declaration in the aspx page, or constructed using the wizard shown in Figure 1.


Figure 1: Building an INSERT statement for the SqlDataSource control.

There is a direct correlation between the fields listed in the SelectCommand and the parameters to the InsertCommand. This will become important later when we discuss the Bind and Eval methods.

To complete the picture, you can place a DetailsView control to the page and set its data source property to the same SqlDataSource associated to the GridView. Set the DefaultMode property of the DetailsView to Insert (see Figure 2) and the AutoGenerateInsertButton property to True. This generates TextBox controls to input data and an Insert link for submitting the data.


Figure 2: Setting the DefaultMode for the DetailsView control.

So far I have only discussed the result you get from dragging and dropping controls, and setting a few properties. Even so, with the implementation thus far you can display results from a table, and edit and update records. This is precisely the benefit of using these controls you can create a fully functioning data entry page in just a few minutes.

 

Working with Template Fields

While dragging and dropping controls makes it easy to create a quick data-entry form, it can be difficult to find the right way to customize control behavior. The DetailsView control has a section that binds the specific rows in the SqlDataSource to fields in the control. As you can see, there is not a lot of flexibility here. For example, there are no physical TextBox controls with which to interact and no apparent extensibility:



 

 

 

 

 

 

 

To access information posted to the page from the DetailsView control, bound columns can be changed to template fields. Template fields allow you to define a custom user interface while still preserving interaction with the parent control. To create a template field, select Edit Fields from the DetailsView smart tag shown in Figure 3.


Figure 3: The Tasks pane from the DetailsView smart tag.

The Fields dialog box will be presented where you can convert a BoundField into a TemplateField. Simply select those fields over which you require greater control and select Convert this field into a TemplateField (see Figure 4).


Figure 4: Converting bound fields into template fields.

In the sample code for this article, I converted the CustomerID, City, Country, and PostalCode fields into template fields (available for download; see end of article for details). The result is that each of those elements are converted to elements, as shown here:



 

   

 

 

   

 

 

   

 

 

For each field an , , and element is provided where you can customize the controls, presentation layout, and bindings applied to each field. To address the reader s question, I am only concerned with the which by default provides me with a TextBox control that I can access during post back. It is always a good idea to rename these controls after converting to a template so you can more easily identify them.

Part of the reader s question is related to recalling user data after an insert command has been executed by the DetailsView control. To address this I ll add a CheckBox control beside each TextBox so users can decide which information they want to keep after each insert during data entry. That s one of the nice features of template fields you can add controls as part of the layout, to collect more information from the user, outside of the basic binding operation. I ll explain how to interact with the controls shortly, but for now, here s an example of the template field after providing friendly names and adding the CheckBox:



 

     

  

  

    

   

  

    

 

 

 

Bind vs. Eval

One of the important things to note about TemplateField is its use of the Bind method. Bind supports two-way data binding for automatic update, insert, and delete operations through the data source control. As an ASP.NET developer you may be more familiar with the Eval method (Databinder.Eval for ASP.NET 1.x), which also binds data to individual controls. The difference between these two methods is subtle. Both bind to a data source for select operations but only Bind supports insert and update operations. Use Eval for data-bound controls that display data and will not be performing inserts and updates.

Hooking Events

After creating template fields you can change the default functionality of the DetailsView control to persist insert data by hooking into some of its events. When you configure the DetailsView control to use insert mode, the data posted back to the page is lost after the insert is completed. The reason for this is simple, although not obvious at first. In insert mode, the SqlDataSource actually binds each field (thus, control) to a new record (which has no data). That s why the data is cleared each time you perform an insert from the DetailsView. You can override this behavior by interacting with the DetailsView control during each post back.

While I m on the subject of customizing the DetailsView for data input, I want to make sure that the input screen is built for speed. To facilitate quick data entry, you usually want to make sure that a specific field has the focus after each insert is completed. For this example, I ll make sure that the CustomerID TextBox gets the focus after each insert. To accomplish this you can add code to the Form_Load event. This can be a bit tricky because the CustomerID TextBox is a child control of the DetailsView. To set focus to a child control you cannot simply use the FindControl method exposed by the Page object. The Page method will only find items that are contained directly on the page. Luckily, data-bound controls have their own FindControl method. So in the Page_Load of the sample code I find the TextBox with ID txtCustomerID and call its Focus method:

protected void Page_Load(object sender, EventArgs e)

{

 // Set the CustomerID with focus for quick input

 TextBox tbCustomerID =

   DetailsView1.FindControl("txtCustomerID") as TextBox;

 tbCustomerID.Focus();

}

Now I ll get back to the code necessary to preserve inserted data after the insert round trip. For this you can hook the ItemInserting event of the DetailsView control. This event will fire whenever a new record is being inserted into the database. This will allow you to capture the contents of each field before the control is bound to a new blank record. In the code sample accompanying this article what I want to do is save the values for each field the user indicated they want the entry screen to remember after an insert. For this I use FindControl on the DetailsView to locate each CheckBox control. For each CheckBox selected, I grab the associated TextBox value out of the DetailsViewInsertEventArgs passed to ItemInserting (see Figure 5).

protected void DetailsView1_ItemInserting(object sender,

 DetailsViewInsertEventArgs e)

   {

    string cityValue;

    string countryValue;

    string postalCodeValue;

    CheckBox cbCity = DetailsView1.FindControl("cityCheck")

     as CheckBox;

    cityValue = (cbCity.Checked == true ?

     e.Values["City"].ToString() : string.Empty);

    Context.Items.Add("city",cityValue.ToString());

    CheckBox cbCountry =

     DetailsView1.FindControl("countryCheck") as CheckBox;

    countryValue = (cbCountry.Checked == true ?

     e.Values["Country"].ToString() : string.Empty);

    Context.Items.Add("country", countryValue.ToString());

    CheckBox cbPostalCode =

     DetailsView1.FindControl("postalCodeCheck")

     as CheckBox;

    postalCodeValue = (cbPostalCode.Checked == true ?

     e.Values["PostalCode"].ToString() : string.Empty);

    Context.Items.Add("postalCode", postalCodeValue.ToString());

}

Figure 5: Hooking the ItemInserting event of the DetailsView to capture values prior to insert.

I need to save these values somewhere accessible to the request after the insert has been executed, so that I can repopulate each DetailsView control with the persisted values. There are several locations where information can be persisted during a request, including view state and session, but in this case the data need only be available during the lifetime of the request. For that reason, I chose the HttpContext object available through HttpContext.Current or through the Page s Context property (among other paths). The Items collection of the HttpContext object is a Hashtable, which is ideal for storing items that only live for the lifetime of a request. As Figure 5 illustrates, I take each column to persist from the DetailsViewInsertEventArgs and save it to the HttpContext Items collection.

After the ItemsInserting event the DetailsView control will perform the insert after which time, as I discussed earlier, the DetailsView fields are cleared as they are bound to a new record. I need a different hook to set each DetailsView control before the updated page is presented to the user. In this case, the DataBound event of the DetailsView control is fired after the DetailsView control binds to a new record. Here I can access the persisted field values from the HttpContext and populate the appropriate TextBox controls in the DetailsView. Figure 6 shows the resulting DataBound event handler.

protected void DetailsView1_DataBound(object sender,

 EventArgs e)

 {

    if (Page.IsPostBack)

    {

    //**************Populate City Textbox

    TextBox tbCity = DetailsView1.FindControl("cityText")

      as TextBox;

    CheckBox cbCity = DetailsView1.FindControl("cityCheck")

      as CheckBox;

    tbCity.Text = Context.Items["city"].ToString();

    cbCity.Checked = (tbCity.Text == string.Empty ?

      false : true);

    //**************Populate Country Textbox

    TextBox tbCountry =

      DetailsView1.FindControl("countryText") as TextBox;

    CheckBox cbCountry =

      DetailsView1.FindControl("countryCheck") as CheckBox;

    tbCountry.Text = Context.Items["country"].ToString();

    cbCountry.Checked = (tbCountry.Text == string.Empty ?

      false : true);

     //**************Populate Postal Textbox

     TextBox tbPostalCode =

      DetailsView1.FindControl("postalCodeText") as TextBox;

    CheckBox cbPostalCode =

      DetailsView1.FindControl("postalCodeCheck") as CheckBox;

     tbPostalCode.Text =

      Context.Items["postalCode"].ToString();

     cbPostalCode.Checked = (tbPostalCode.Text ==

      string.Empty ? false : true);

   }

 } 

Figure 6: Hooking the DataBound event to populate controls after insert.

 

Figure 7 illustrates the DetailsView after making the changes discussed in this article. The result is that setting the focus on the first TextBox allows the user to easily tab through and enter the new rows. The CheckBox control allows users to decide which fields to retain after completing an insert, for repetitive data entry sessions.

 


Figure 7: DetailsView with template fields.

 

Conclusion

Data-bound controls really do make life easier for the developer, in particular for simple operations, and they are a great way to get data-centric pages prototyped with ease. When you want to customize the default behaviors of these controls, template fields and control events are incredibly useful.

If you have additional questions about this or other ASP.NET topics, drop us a line at [email protected]. Thanks for reading!

The source code accompanying this article is available for download.

Daniel N. Egan, an MCT, MCSD, and Microsoft ASP.NET MVP, held a variety of positions in the information technology and engineering fields before starting Odyssey Consulting Group Inc. (http://www.OCGPros.com), where he serves as President and Chief Architect. In addition to his development work, Daniel teaches a .NET Certification course and serves on the .NET Advisory board at California State University, Fullerton. He also is the co-founder of the SoCalDotNet Developers Group. Daniel is a frequent speaker and conference presenter, has written several articles, and is the author of Building Websites with VB.NET and DotNetNuke 3.0 (Packt Publishing). To find out more about Daniel, visit his blogs: Dot Net Doc (http://www.DotNetDoc.com), a .NET and DNN developer resource blog, or ThePatternMan (http://www.ThePatternMan.com), which focuses on Design Patterns in .NET.

 

 

 

Hide comments

Comments

  • Allowed HTML tags: <em> <strong> <blockquote> <br> <p>

Plain text

  • No HTML tags allowed.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.
Publish