The Code-Behind Approach

Writing Custom Controls

User controls are good at prototyping new controls that have a complex user interface. They are the perfect solution when you need to combine two or more controls. As long as the structure of the new controls is stable during the lifetime of the program, user controls offer you an excellent mix of programming ease and productivity. The architectural principle lying behind a user control is aggregation rather than inheritance. If you need only an enhanced or stripped-down version of an existing control, you are better off writing a new class that inherits from it.

A Web control has varying degrees of complexity. You can write a simple Web control that outputs HTML code, but you can also write a more complex composite control that dynamically creates and lays out a tree of child controls. You can also devise templated controls, which let you specify user-defined ASP.NET templates so that you can populate parts of the user interface.

All ASP.NET controls can be extended through inheritance. Depending on how complex you want the new control to be, this extension can be extremely simple.

A Labeled TextBox Control

Earlier in this chapter, I built a user control made up of a label and a text box that you could independently program. To make it fully programmable, I exposed both the label and the text box as public properties of the Label type and TextBox type, respectively. If you want your users to have total control of the child elements, this approach works just fine. When you want to implement finer control on the properties of the constituent label and text box controls, you must understand that user controls have a structural limitation: constituent elements are aggregated in the all-encompassing container control. Constituent controls can either be exposed as-is as a property on the parent control or be completely hidden by the parent control. In the latter case, a child control could expose one or more of its properties through proxy properties defined on the parent control, as shown in Figure 5-9.

Figure 5-9

Exposing constituent control properties within a user control.

If you expose constituent elements of the user control, you cannot filter the child control s programmability any longer on a control-by-control basis. By contrast, if you don t expose child controls, you have to explicitly define any element of the interface as a distinct property. Basically, you re faced with rebuilding a brand new programming interface rather than extending or enhancing the existing one. Let s rewrite the LabelBox control as a custom control, that is, a class that inherits from TextBox.

note

The LabelBox control is made up of a label and a text box control situated side by side. So, at least in principle, you could build your interface starting with Control as well as Label or TextBox. No syntax rule suggests that one of these alternatives is the correct one. It is mostly a matter of preference and vision. In my implementation, I consider the LabelBox control as a sort of extended text box and build it using only the TextBox class.

public class LabelBox : TextBox { public LabelBox() {} public LabelBox(String strLabel, String strText) { Label = strLabel; Text = strText; } public String Label = ""; public String LabelCssClass = ""; public Boolean LabelOnTop = false; 

The preceding code snippet shows the two constructors of the new class, one of which takes no argument. The LabelBox control has three properties: the text, the CSS style of the label, and a Boolean flag denoting whether the label has to be rendered above the control or next to it. A private instance of the Label class represents the label, which is created and configured in the override of the CreateChildControls method.

private Label m_lbl; protected override void CreateChildControls() { m_lbl = new Label(); if (LabelOnTop) m_lbl.Text = Label + "<br>"; else m_lbl.Text = Label + "&nbsp;&nbsp;"; m_lbl.Font.Bold = true; m_lbl.CssClass = LabelCssClass; // Set standard styles for the text box this.Font.Name = "verdana"; this.Font.Size = FontUnit.XSmall; this.Style["border"] = "solid 1px black"; }

The control renders itself through the Render method. In the implementation of this method, the new control outputs any HTML code that forms its user interface. In our example, you should render the label control and call the Render method of the base class.

protected override void Render(HtmlTextWriter writer) { m_lbl.RenderControl(writer); base.Render(writer); }

Using Custom Controls

A custom control is a compiled element that you register with pages by using assemblies. You use the @ Register directive and are required to specify the assembly name (without an extension) and the namespace. You don t have to indicate a tag name because this defaults to the class name. You indicate instead the namespace prefix:

<%@ Register TagPrefix="expo" Namespace="BWSLib" Assembly="BWSLib.Controls.LabelBox" %>

The control s declaration in the body of an ASP.NET page looks nearly identical to the declaration of a standard control or user control:

<expo:LabelBox runat="server" Label="Inline Label" Text="esposito" LabelTop="true" LabelCss />

Because the LabelBox control derives from TextBox, you can use any of the base properties in addition to your custom ones.

note

Although you need a compiled assembly to deploy a custom control and a simple text file to deploy a user control, there is no relevant difference in performance between the two. User controls are compiled on the fly and then cached, so after the first use they can offer the same performance as other controls. The big difference between them is their design: user controls exploit aggregation whereas custom controls are truly object-oriented and leverage inheritance.

Figure 5-10 shows the LabelBox control in action in a sample page. The full source code for the LabelBox.cs and TestLabelBox.aspx applications is available on the companion CD.

note

When writing a custom control, you can override not only methods of the parent control but also properties. For example, you can override a property to make it read-only or to enhance its standard behavior. The following code shows how to modify ReadOnly to make it change the text box s background when set:

public override bool ReadOnly { get {return base.ReadOnly;} set {base.ReadOnly=value; BackColor=(value ?Color.White :Color.Beige;)} }

Figure 5-10

Two LabelBox controls in action.

An Off-the-Shelf DataGrid Control

In the previous chapters, I illustrated a variety of the DataGrid control s features, including pagination, sorting, templates, and in-place editing. When you are writing code and the DataGrid control is involved, in most situations you need to handle the events raised by the control. You do this by writing C# or Visual Basic code to flesh the <script> section out (the so-called code noodle that I mentioned earlier in this chapter when discussing ASP.NET lasagne code).

As you know, the objective of the DataGrid control is to show and sort pages of records that are formatted in columns of data. In spite of this declared intention, you still have to write several lines of code. Why? Because we are talking for the most part about Web controls. A Web control is a software component that springs to life when the ASP.NET page is processed on the Web server by a special module, the HTTP runtime. A Web control generates output composed of HTML tags and elements. The user s client interacts with the HTML output of the Web controls. When an event occurs on the client (for example, a new grid page is requested), the page is posted back to the server. Next, the HTTP runtime restores the previously saved state, gives Web controls a chance to make this state consistent with their further expectations, and resumes server-side processing. When the DataGrid control is the Web control in question, a number of issue arise:

  • For performance reasons, the grid s data source is not persisted across multiple requests of the same page.

  • No information about the current sort expression is stored.

  • No information about the columns that actually form the grid is stored.

As a result, to have a grid that moves through pages and sorts by columns, you have to write code every time you do the following:

  • Retrieve and bind the data source.

  • Sort the data source.

  • Set the new page index.

In most cases, the code you need is boilerplate code, but you do still have to write it or, even worse, cut and paste it from existing pages. When you need to consider reusability, the natural question is, why not embed all or part of this code you ve written into a new, wrapped-up, ad-hoc DataGrid control? The answer is, why not?

No architectural or design issues prevent you from implementing an enhanced grid, but you should be aware of some critical decisions you must make. One of these decisions is particularly important: how you retrieve, and possibly sort, the data. As discussed in Chapter 2, scalability is a serious issue for Web applications, so you ll need to answer the retrieval and sorting question based on the specific features of your application. You might want to cache the data source in Cache or Session rather than using XML files. You might also decide that, all in all, you can afford reloading data page by page by using super-optimized stored procedures. If your requirements are to display the most current data, you can even employ ADO server cursors to provide an up-to-date view of data at any time. You can see that there is more than one right way to store and retrieve data. What works for desktop applications does not work as well over the Web. That s why the DataGrid Web control has been designed with such a flexible programming interface.

caution

The DataGrid Web control is radically different from the DataGrid Windows control. They belong to distinct namespaces: System.Web.UI.WebControls for the Web control and System.Windows.Forms for the Windows control. They have similar programming interfaces, but they are not identical or even functionally equivalent. The Windows Forms control features more advanced characteristics that have not been implemented in the Web s counterpart. To accomplish the same task with both controls, you often must use different code. Bear this in mind when digging into the documentation.

All the aforementioned considerations do not necessarily lead to the conclusion that code-behind is the only way to lighten the code burden for an ASP.NET page based on the DataGrid control. However, to write an enhanced, ready-to-use version of the DataGrid control, you must decide how to solve the three issues just mentioned: paging, sorting, and data retrieval.

To handle paging, in most cases, you don t need to do much more than set the new page index in the PageIndexChanged event, and you can hard-code this behavior in the new control. As for sorting, a common practice is to sort data using a DataView class, which ultimately provides the grid s contents. You can hard-code in the grid s internal code the foundation for auto-reverse and safe multifield sorting. With respect to data retrieval in a new control, I believe that this is an application-specific choice. In light of this, the best you can do is fire an event that alerts the code to refresh the grid s view. Paging and sorting will be provided automatically but you are still responsible for binding the code to data. Such a control (named PowerGrid in our example) can have a few extra properties and require only the following code for the output shown in Figure 5-11:

<expo:PowerGrid runat="server" AutoGenerateColumns="false" PageSize="3" > <Columns> <asp:BoundColumn DataField="employeeid" HeaderText="ID" SortExpression="employeeid" /> <asp:BoundColumn DataField="firstname" HeaderText="First Name" /> <asp:BoundColumn DataField="lastname" HeaderText="Last Name" SortExpression="lastname" /> </Columns> </expo:PowerGrid>

Figure 5-11

The PowerGrid control in action.

Wrapping up the DataGrid control in a new, more sophisticated control allows you to embed your graphical preferences colors, fonts, borders, styles, or whatever in the control s code. The PowerGrid class inherits from DataGrid and sets a lot of visual attributes in the class constructor. It also hooks up a few events, as the next bit of code shows:

ItemCreated += new DataGridItemEventHandler(OnItemCreated); SortCommand += new DataGridSortCommandEventHandler(OnSortCommand); PageIndexChanged += new DataGridPageChangedEventHandler(OnPageChanged);

The ItemCreated handler adjusts the header to reflect the current sort expression and updates the pager bar. SortCommand is responsible for preparing the sort expression, taking into account directions and fields. PageIndexChanged cares about the index of the new page. Both selecting a new page and sorting by a new field cause the grid to retrieve the data and refresh the view. The code for refreshing the view, however, is left to the calling page and is not implemented in the control s code. Whenever the grid needs to rebind and refresh the view, it fires a custom event: UpdateView.

The UpdateView Event

The following code declares the UpdateView event. In our example, you can use the EventArgs class, because all the information the calling page needs is available through the DataGrid control.

public event EventHandler UpdateView; protected virtual void OnUpdateView(EventArgs e) { if (UpdateView != null) UpdateView(this, e); }

The UpdateView event is fired to rebind and refresh the content of the page. The following code shows how this is done for the PageIndexChanged event:

void OnPageIndexChanged(Object sender, DataGridPageChangedEventArgs e) { CurrentPageIndex = e.NewPageIndex; // Fire page-level event: UpdateView OnUpdateView(EventArgs.Empty); }

The event is handled by the client, just as any other event would be.

<expo:PowerGrid runat="server"  OnUpdateView="UpdateView">

The signature of the handler is standard.

public void UpdateView(Object sender, EventArgs e) { UpdateDataView(); }

Unless the data you retrieve from the source is already sorted, the Update View handler needs to know about which sort expression to use. Unfortunately, the DataGrid control does not provide this information.

The SortExpression Property

In Chapter 2, when I discussed the sorting capabilities of the DataGrid control, I remarked how tricky auto-reverse sorting can be. In our example, you need to compare the current sort expression with the new sort expression and just reverse the order if the expressions match. I used the ViewState collection of the DataGrid control to persistently store this information. The PowerGrid control accesses this ViewState slot by using a read-only property named SortExpression:

public String SortExpression { get { return (String) ViewState["CurrentSortExpression"]; } }

The following code looks like some code you have already seen in previous chapters that refresh grid views. The key functionality here is the retrieval of the sort expression via a custom property of the PowerGrid control. The full source code for the PowerGrid.cs, TestPowerGrid.cs, and TestPowerGrid.aspx applications is available on the companion CD.

private void UpdateDataView() { DataSet ds = (DataSet) Session["MyDataSet"]; DataView dv = ds.Tables["MyTable"].DefaultView; dv.Sort = grid.SortExpression; grid.DataSource = dv; grid.DataBind(); }

The PowerGrid control takes advantage of the reusability features of ASP.NET to provide you with a powerful and specialized control that uses minimal code. By combining custom controls with code-behind techniques, you can have lightly coded ASP.NET pages that separate code and layout and offer a good level of reusability.



Building Web Solutions with ASP. NET and ADO. NET
Building Web Solutions with ASP.Net and ADO.NET
ISBN: 0735615780
EAN: 2147483647
Year: 2002
Pages: 75
Authors: Dino Esposito

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net