Chapter 4 includes a chart of the five types of controls supported in ASP.NET: HTML controls, HTML server controls, web server controls, validation controls, and controls created by the developer. This chapter will discuss this last type of control, known as custom controls, and a subset of them called user controls.
Custom controls
are compiled controls that act, from
the client’s perspective, much like web (ASP)
controls. Custom controls can be created in one of three ways:
By deriving a new custom control from an existing control (e.g.,
deriving your own specialized text box from
asp:textbox
). This is known as a derived
custom control
.
By composing a new custom control out of two or more existing
controls. This is known as a composite custom
control
.
By deriving from the base control class, thus creating a new custom
control from scratch. This is known as a full custom
control
.
Of course, all three of these methods, and the three control types
that correspond to them, are variations on the same theme.
We’ll consider these custom controls later in this
chapter. The simplest category of custom controls is a subset called
usercontrols. Microsoft distinguishes user controls
as a special case because they are quite different from other types
of custom controls. In short, user controls
are
segments of ASP.NET pages that can be reused from within other pages.
This is similar to “include files”
familiar to ASP developers. However, user controls are far more
powerful. User controls support properties and events, and thus
provide reusable functionality as well as reusable HTML.
User controls allow you to save a part of an existing ASP.NET page
and reuse it in many other ASP.NET pages. A user control is almost
identical to a normal .aspx page,
with two differences: the
user control has the .ascx extension rather than .aspx, and it may not have
<HTML>
, <Body>
, or
<Form>
tags.
The simplest user control is one that displays HTML only. A classic example of a simple user control is an HTML page that displays a copyright notice. Example 14-1 shows the complete listing for copyright.ascx .
Example 14-1. copyright.ascx
<%@ Control %> <hr> <table> <tr> <td align="center">Copyright 2005 Liberty Associates, Inc.</td> </tr> <tr> <td align="center">Support at http://www.LibertyAssociates.com</td> </tr> </table>
To see this at work, you’ll modify Example 8-1, adding just two lines. At the top of the .aspx file, you’ll add the registration of your new user control:
<%@Register tagprefix="OReilly" Tagname="copyright" src="copyright.ascx" %>
This registers the control with the page and establishes both the
prefix (OReilly) and the TagName (copyright). In the page you are
modifying, there are any number of ASP elements such as
<asp:ListItem>
. The letters
asp
before the colon are the
tag prefix that identifies this tag as
being a web control, and the token ListItem
is the
TagName.
Your user controls will have a tag prefix as well (in this case,
Oreilly)
, in addition to a specific tag name (in
this case, copyright)
. Interestingly, there is
nothing in the .ascx file itself
that identifies the tag prefix.
The modified .aspx code is shown in Example 14-2. The output from this page is shown in Figure 14-1.
Example 14-2. Modification of Example 8-1
<%@ Page language="c#" Codebehind="WebForm1.aspx.cs" AutoEventWireup="false" Inherits="Validation04.WebForm1" %><%@Register tagprefix="OReilly" Tagname="copyright" src="copyright.ascx" %>
<HTML> <HEAD> <!-- Demonstrate simple required field validation --> <meta name=vs_targetSchema content="Internet Explorer 5.0"> <meta name="GENERATOR" Content="Microsoft Visual Studio 7.0"> <meta name="CODE_LANGUAGE" Content="C#"> </HEAD> <body> <h3> <font face="Verdana">Bug Report</font> </h3> <form runat="server" ID="frmBugs"> <table bgcolor=gainsboro cellpadding=10> <tr valign="top"> <td colspan=3> <!-- Display error messages --> <asp:Label ID="lblMsg" Text="Please report your bug here" ForeColor="red" Font-Name="Verdana" Font-Size="10" runat=server /> <br> </td> </tr> <tr> <td align=right> <font face=Verdana size=2>Book</font> </td> <td> <!-- Drop down list with the books (must pick one) --> <ASP:DropDownList id=ddlBooks runat=server> <asp:ListItem>-- Please Pick A Book --</asp:ListItem> <asp:ListItem>Programming ASP.NET</asp:ListItem> <asp:ListItem>Programming C#</asp:ListItem> <asp:ListItem> Teach Yourself C++ In 21 Days </asp:ListItem> <asp:ListItem> Teach Yourself C++ In 24 Hours </asp:ListItem> <asp:ListItem>TY C++ In 10 Minutes</asp:ListItem> <asp:ListItem>TY More C++ In 21 Days</asp:ListItem> <asp:ListItem>C++ Unleashed</asp:ListItem> <asp:ListItem>C++ From Scratch</asp:ListItem> <asp:ListItem>XML From Scratch</asp:ListItem> <asp:ListItem>Web Classes FS</asp:ListItem> <asp:ListItem>Beg. OO Analysis & Design</asp:ListItem> <asp:ListItem>Clouds To Code</asp:ListItem> <asp:ListItem> CIG Career Computer Programming </asp:ListItem> </ASP:DropDownList> </td> <!-- Validator for the drop down --> <td align=middle rowspan=1> <asp:RequiredFieldValidator id="reqFieldBooks" ControlToValidate="ddlBooks" Display="Static" InitialValue="-- Please Pick A Book --" Width="100%" runat=server> Please choose a book </asp:RequiredFieldValidator> </td> </tr> <tr> <td align=right> <!-- Radio buttons for the edition --> <font face=Verdana size=2>Edition:</font> </td> <td> <ASP:RadioButtonList id=rblEdition RepeatLayout="Flow" runat=server> <asp:ListItem>1st</asp:ListItem> <asp:ListItem>2nd</asp:ListItem> <asp:ListItem>3rd</asp:ListItem> <asp:ListItem>4th</asp:ListItem> </ASP:RadioButtonList> </td> <!-- Validator for editions --> <td align=middle rowspan=1> <asp:RequiredFieldValidator id="reqFieldEdition" ControlToValidate="rblEdition" Display="Static" InitialValue="" Width="100%" runat=server> Please pick an edition </asp:RequiredFieldValidator> </td> </tr> <tr> <td align=right style="HEIGHT: 97px"> <font face=Verdana size=2>Bug:</font> </td> <!-- Multi-line text for the bug entry --> <td style="HEIGHT: 97px"> <ASP:TextBox id=txtBug runat=server width="183px" textmode="MultiLine" height="68px"/> </td> <!-- Validator for the text box--> <td style="HEIGHT: 97px"> <asp:RequiredFieldValidator id="reqFieldBug" ControlToValidate="txtBug" Display="Static" Width="100%" runat=server> Please provide bug details </asp:RequiredFieldValidator> </td> </tr> <tr> <td> </td> <td> <ASP:Button id=btnSubmit text="Submit Bug" runat=server /> </td> <td> </td> </tr> </table> </form><OReilly:copyright runat="server" />
</body> </HTML>
In Figure 14-1 the horizontal rule at the bottom of the page, and the copyright notice below it, comes from the .ascx user control you’ve created. This control can be reused in many pages. If you update the copyright, you will make that update only in the one .ascx file, and it will be displayed appropriately in all the pages that use that control.
In the next example, you will recreate the book drop-down list itself, this time as a user control. The process of converting part of an existing HTML page into a user control is very simple; you just extract the code that creates the drop-down list into its own HTML file and name that file with the .ascx extension.
Visual Studio
.NET provides support for creating user controls. Right-click on the
project and choose Add → Add New Item. One of the
choices is New User Control. This choice opens a new form. The HTML
at the top of the form includes the Control
directive, which sets the language attributes, etc.
Rename the new item
BookList.ascx
and
copy in the code for creating the book list, as shown in Example 14-3.
Example 14-3. The BookList user control
<!-- Drop down list with the books (must pick one) --> <ASP:DropDownList id=ddlBooks runat=server> <asp:ListItem>-- Please Pick A Book --</asp:ListItem> <asp:ListItem>Programming ASP.NET</asp:ListItem> <asp:ListItem>Programming C#</asp:ListItem> <asp:ListItem> Teach Yourself C++ In 21 Days </asp:ListItem> <asp:ListItem> Teach Yourself C++ In 24 Hours </asp:ListItem> <asp:ListItem>TY C++ In 10 Minutes</asp:ListItem> <asp:ListItem>TY More C++ In 21 Days</asp:ListItem> <asp:ListItem>C++ Unleashed</asp:ListItem> <asp:ListItem>C++ From Scratch</asp:ListItem> <asp:ListItem>XML From Scratch</asp:ListItem> <asp:ListItem>Web Classes FS</asp:ListItem> <asp:ListItem>Beg. OO Analysis & Design</asp:ListItem> <asp:ListItem>Clouds To Code</asp:ListItem> <asp:ListItem> CIG Career Computer Programming </asp:ListItem> </ASP:DropDownList>
In Example 14-3, you’ll strip out the validator code, to keep the focus on working with the user control. The validator can be used with the user control exactly as it was with the ASP control.
To make this work in your page, you’ll add a new
Register
statement:
<%@Register tagprefix="OReilly" Tagname="bookList" src="bookList.ascx" %>
In the body of the page, you’ll add the new user control, exactly where you cut the original code:
<OReilly:bookList runat="server" ID="Booklist"/>
So far, all you’ve put into the user control is straight HTML. This is simple, but also somewhat limited. There is no reason to so severely limit the user control. In the next example, you’ll add support for filling the list box with books from a database. To do so, you’ll need to add a table to the ProgASPNetBugs database, as shown in Figure 14-2.
You’ll also need to populate this table with the values shown in Figure 14-3, or simply download the entire database with data already provided.
You are ready to fill the list box dynamically, usingdata binding. Strip out all the code that fills the list box by hand. This reduces Example 14-3 to two lines:
<ASP:DropDownList id=ddlBooks runat=server> </ASP:DropDownList>
The key to making this work is now in the Control
tag:
<%@ Control Language="c#" AutoEventWireup="false" Codebehind="BookList.ascx.cs" Inherits="UserControl1.WebUserControl1"%>
The Codebehind
attribute points to the code-behind
page. In that page, you’ll add code to the Page_Load
method to bind the list box to the appropriate table in the database.
That code is shown in Example 14-4 for C# and Example 14-5 for VB.NET.
Example 14-4. C# Page_Load from the code-behind page for the user control
private void Page_Load(object sender, System.EventArgs e) { if (!IsPostBack) { string connectionString = "server= " + ServerName + "; uid=sa;pwd=" + Password + "; database= " + DB; // get records from the Bugs table string commandString = "Select BookName from Books"; // create the data set command object // and the DataSet SqlDataAdapter dataAdapter = new SqlDataAdapter( commandString, connectionString); DataSet dataSet = new DataSet( ); // fill the data set object dataAdapter.Fill(dataSet,"Bugs"); // Get the one table from the DataSet DataTable dataTable = dataSet.Tables[0]; ddlBooks.DataSource = dataTable.DefaultView; ddlBooks.DataTextField = "BookName"; ddlBooks.DataBind( ); } }
Example 14-5. VB.NET Page_Load from the code-behind page for the user control
Private Sub Page_Load(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles MyBase.Load If Not IsPostBack Then Dim connectionString As String = _ "server= " & ServerName & "; uid=sa;pwd=" + Password + "; database= " + DB; ' get records from the Bugs table Dim commandString As String = _ "Select BookName from Books" ' create the data set command object ' and the DataSet Dim dataAdapter as SqlDataAdapter = _ new SqlDataAdapter( _ commandString, connectionString); Dim dataSet As DataSet = New DataSet( ) ' fill the data set object dataAdapter.Fill(dataSet, "Books") ' Get the one table from the DataSet Dim dataTable As DataTable = dataSet.Tables(0) ddlBooks.DataSource = dataTable.DefaultView ddlBooks.DataTextField = "BookName" ddlBooks.DataBind( ) End If End Sub
The host page does not change at all. The updated user control now works as intended, loading the list of books from the database, as shown in Figure 14-4.
There
can
be only one
@Control
directive for each user control. This
attribute is used by the ASP.NET page parser and compiler to set
attributes for your user control. Possible values are shown in Table 14-1.
Table 14-1. Values for @Control properties
Attribute |
Description |
Possible values |
---|---|---|
|
| |
The class name for the page. |
Any valid class name. | |
Passed to compiler. |
Any valid compiler string indicating options. | |
Whether to compile with debug symbols. |
| |
Text description of the page. |
Any valid text. | |
Is view state maintained for the user control? |
| |
Should page be compiled with VB.NET option explicit |
| |
Defines a code-behind class. |
Any class derived from UserControl. | |
The language used for inline rendering and server-side script blocks. |
Any .NET-supported language. | |
Page should be compiled using VB.NET |
| |
Name of the source file for the code-behind. |
Any valid filename. | |
Compiler warning level at which compilation will abort. |
|
You can make your user control far more powerful by adding properties. Properties allow your client (in this case WebForm1) to interact with your control, setting attributes either declaratively (when the user control is added to the form) or programmatically (while the program is running).
You can, for example, give your book list control properties for the server name, the password, and the database to which you will connect. You do this in four steps:
Create a property. You must decide if you will provide a read-write, read-only, or write-only property. For this example, you’ll provide read-write properties.
Provide an underlying value for the property. You can do this by computing the property, retrieving it from a database or, as you’ll do here, storing the underlying value in a private member variable. You must also decide if you’ll provide a default value for your properties.
Integrate the underlying values into the body of the code.
Set the property from the client, either declaratively (as an attribute) or programmatically.
There is nothing special about the property for the user control; you create it as you would any property for a class. In C#, this takes the form:
public string ServerName { get { return serverName; } set { serverName = value; } } public string Password { get { return password; } set { password = value; } } public string DB { get { return db; } set { db = value; } }
In VB.NET, the code is:
Public Property ServerName As String Get Return sServerName End Get Set sServerName = Value End Set End Property Public Property Password As String Get Return sPassword End Get Set sPassword = Value End Set End Property Public Property DB As String Get Return sDB End Get Set sDB = Value End Set End Property
Note that you can take advantage of C#’s case-sensitivity to differentiate the property name (such as ServerName) from the private variable representing the underlying property value (such as serverName). However, because VB.NET is case-insensitive, you must use a property name (such as ServerName) that is clearly distinctive from the private variable holding the underlying property value (such as sServerName).
When coding in C#, we tend to prefer the more extended property declaration style, as shown with ServerName. However, in this book we often use the terser form to save space, as shown for Password and DB.
You certainly can compute the value of a property, or look up the value in a database. In this example, however, you’ll simply create member variables to hold the underlying value. In C#, the code is:
private string serverName; private string password; private string db = "ProgASPDotNetBugs";
In VB.NET, it’s:
Private sServerName, sPassword As String Private sDB As String = "ProgASPDotNegBugs"
Acting in the role of control designer, you have decided to provide a default value for the db property (the name of the database), but you have not provided a default value for the name of the server or the sa (system administrator) password. This is appropriate; you can safely assume the database is ProgASPDotNetBugs, but you can’t possibly know in advance what database server will be used, or what the sa password is.
Having declared the properties, you must now modify the connection string to use the properties, rather than the hard-coded values. In C#, the code is:
string connectionString = "server= " + serverName + "; uid=sa;pwd=" + password + "; database= " + db;
and in VB.NET, it’s:
Dim connectionString As String = _ "server= " & sServerName & _ "; uid=sa;pwd=" & _ sPassword & "; database= " & sDB
Here you concatenate hard-coded string values
(“server=”) with the member
variables that will be set through the properties. You could, as an
alternative, just use the properties’
Get
accessors, rather than using the underlying
values:
string connectionString = "server= " + ServerName + "; uid=sa;pwd=" + Password + "; database= " + DB;
While using the underlying value is trivially more efficient, using the property has the advantage of allowing you to change the implementation of the property without breaking this code.
In the client you
must now provide values for the two required
attributes,
ServerName
and
Password
, and you
may provide a value for the DB property. For
example, you might write:
<OReilly:bookList runat="server" ID="Booklist" DB="ProgASPDotNetBugs" Password="yourPassWord" ServerName="YourServer"
Notice that in the preceding code, you have provided a value for the DB property. This code will continue to work if you leave out this attribute, but adding it makes the code self-documenting.
Event handling with user controls can be a bit confusing. Within a user control (e.g., bookList), you may have other controls (e.g., a list box). If those internal controls fire events, you’ll need to handle them within the user control itself. The page the user control is placed in will never see those events.
That said, a user control itself can raise events. You may raise an event in response to events raised by internal controls, in response to user actions or system activity, or for any reason you choose.
You
declare new events for the user
control just as you would for any class. Example 14-6
shows the complete code listing for
BookList.ascx.cs
.
Example 14-6. BookList.ascx.cs
namespace UserControl2A1 { using System; using System.Data; using System.Data.SqlClient; using System.Drawing; using System.Web; using System.Web.UI.WebControls; using System.Web.UI.HtmlControls; public abstract class BookList : System.Web.UI.UserControl { protected System.Web.UI.WebControls.DropDownList ddlBooks; private string serverName; private string password = "oWenmEany"; private string db = "ProgASPDotNetBugs"; public delegate void ListChangedHandler(object sender, EventArgs e); public event ListChangedHandler ListChanged; protected virtual void OnListChanged(EventArgs e) { if (ListChanged != null) ListChanged(this, e); } public string ServerName { get { return serverName; } set { serverName = value; } } public string Password { get { return password; } set { password = value; } } public string DB { get { return db; } set { db = value; } } public BookList( ) { this.Init += new System.EventHandler(Page_Init); } private void Page_Load(object sender, System.EventArgs e) { if (!IsPostBack) { string connectionString = "server= " + ServerName + "; uid=sa;pwd=" + Password + "; database= " + DB; // get records from the Bugs table string commandString = "Select BookName from Books"; // create the data set command object // and the DataSet SqlDataAdapter dataAdapter = new SqlDataAdapter( commandString, connectionString); DataSet dataSet = new DataSet( ); // fill the data set object dataAdapter.Fill(dataSet,"Bugs"); // Get the one table from the DataSet DataTable dataTable = dataSet.Tables[0]; ddlBooks.DataSource = dataTable.DefaultView; ddlBooks.DataTextField = "BookName"; ddlBooks.DataBind( ); } } private void Page_Init(object sender, EventArgs e) { InitializeComponent( ); } #region Web Form Designer generated code /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// </summary> private void InitializeComponent( ) { this.ddlBooks.SelectedIndexChanged += new System.EventHandler(this.OnSelectedIndexChanged); this.Load += new System.EventHandler(this.Page_Load); } #endregion public class BookListArgs : EventArgs { public string bookSelected; } private void OnSelectedIndexChanged( object sender, System.EventArgs e) { OnListChanged(e); } } }
You start by declaring a delegate that describes the event procedure:
public delegate void ListChangedHandler(object sender, EventArgs e);
You then declare the event itself:
public event ListChangedHandler ListChanged;
You must create a method that begins with the letters “On” followed by the name of the event, as follows:
protected virtual void OnListChanged(EventArgs e) { }
This method typically checks that the event has one or more handlers registered, and if so, it raises the event, as the following code shows:
protected virtual void OnListChanged(EventArgs e) { if (ListChanged != null) ListChanged(this, e); }
You are now ready to test the event. For this example, go back to the list box within the book list user control and add an event handler for the selected item being changed:
private void OnSelectedIndexChanged (object sender, System.EventArgs e) { OnListChanged(e); }
When the item is changed, you call the
OnListChanged
method, which in turn fires the
ListChanged event. More about
this shortly.
Your web page can add an event handler for its BookList element. The declaration in the .aspx page is unchanged:
<td><OREILLY:BOOKLIST id=Booklist runat="server" ServerName="yourServer" Password="yourPW" DB="ProgASPDotNetBugs"></OReilly:bookList></TD></TR>
The code-behind changes, however. To register the event, you’ll need an instance of a booklist object:
protected UserControl3.BookList Booklist;
You now have only to register the event
handler. Within the
InitializeComponent
method of WebForm1.aspx.cs
, add this code:
this.Booklist.ListChanged += new UserControl3.BookList.ListChangedHandler(this.Booklist_ListChanged);
The event handler Booklist_ListChanged is thus wired to the ListChanged event of the booklist. When the user chooses a book, the internal list box fires a postback event, the OnSelectedIndexChanged event fires within the .ascx page, and the OnSelectedIndexChanged event handler within the user control responds.
When the ListChanged event is fired, it is caught in the containing page’s BookList_ListChanged method, and the label is updated:
public void Booklist_ListChanged(object sender, System.EventArgs e) { lblMsg.Text = "The list changed!!"; }
To the user, it appears just as it should; the list box within the user control appears just to be another control with which the user can interact, as shown in Figure 14-5.
Defining
a custom event, trapping the
DropDownList control’s SelectedIndexChanged event,
raising a custom event, and handling it within the
.aspx
page are very
easy and require fewer lines of code in VB.NET than in C#. Within the
class definition of the BookList control, you simply declare the
event and its signature, as follows:
Public Event ListChanged(ByVal sender As Object, ByVal e As EventArgs)
Since Visual Studio automatically declares the DropDownList control
in the code-behind file using the
WithEvents
keyword, the control’s
events are automatically trapped by VB.NET, and any event handler, if
one is present, is executed. Hence, you simply need to define the
following event handler, which raises the custom
ListChanged event:
Private Sub ddlBooks_SelectedIndexChanged(ByVal sender As Object, _ ByVal e As System.EventArgs) _ Handles ddlBooks.SelectedIndexChanged RaiseEvent ListChanged(sender, e) End Sub
That’s all the code that’s required
in the user control’s code-behind file. In the
ASP.NET application, you have to declare the instance of the
Booklist
class using the
WithEvents
keyword:
Protected WithEvents Booklist1 As UserControl1VB.Booklist
The final step is to provide the event handler, as follows:
Private Sub Booklist1_ListChanged(ByVal sender As System.Object, _ ByVal e As System.EventArgs) _ Handles Booklist1.ListChanged lblMsg.Text = "The list changed!!" End Sub
The complete code-behind page for the Booklist user control is shown in Example 14-7.
Example 14-7. The VB.NET version of the Booklist user control’s code-behind file
Imports System.Data.OleDb Public MustInherit Class Booklist Inherits System.Web.UI.UserControl Protected WithEvents ddlBooks As System.Web.UI.WebControls.DropDownList Public Event ListChanged(ByVal sender As Object, ByVal e As EventArgs) #Region " Web Form Designer Generated Code " 'This call is required by the Web Form Designer. <System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent( ) End Sub Private Sub Page_Init(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles MyBase.Init 'CODEGEN: This method call is required by the Web Form Designer 'Do not modify it using the code editor. InitializeComponent( ) End Sub #End Region Private Sub Page_Load(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles MyBase.Load If Not IsPostBack Then Dim connectionString As String = _ "server= " & ServerName & "; uid=sa;pwd=" & Password & "; database= " & DB ' get records from the Bugs table Dim commandString As String = _ "Select BookName from Books" ' create the data set command object ' and the DataSet Dim dataAdapter as SqlDataAdapter = _ new SqlDataAdapter( _ commandString, connectionString); Dim dataSet As DataSet = New DataSet( ) ' fill the data set object dataAdapter.Fill(dataSet, "Books") ' Get the one table from the DataSet Dim dataTable As DataTable = dataSet.Tables(0) ddlBooks.DataSource = dataTable.DefaultView ddlBooks.DataTextField = "BookName" ddlBooks.DataBind( ) End If End Sub Private Sub ddlBooks_SelectedIndexChanged(ByVal sender As Object,_ ByVal e As System.EventArgs) _ Handles ddlBooks.SelectedIndexChanged RaiseEvent ListChanged(sender, e) End Sub End Class
It would be even more useful if the control could tell the page what book was chosen. The idiom for doing so is to provide a custom event argument type derived from System.EventArgs. To accomplish this, you’ll add a class declaration nested within the Booklist class. In C#, this takes the form:
public class BookListArgs : EventArgs { public string bookSelected; }
In VB.NET:
Public Class BookListArgs Inherits EventArgs Public bookSelected As String End Class
You can now declare the event to use this new type of Event argument.
In C#, you do this by modifying
the delegate
statement:
public delegate void ListChangedHandler(object sender, BookListArgs e);
In VB.NET, you modify the Event
statement:
Public Event ListChanged(ByVal sender As Object, ByVal e As BookListArgs)
In C#, the event handler for the list box change event is now updated to get the selected item’s text and add that to the BookListArgs object’s bookSelected property:
private void OnSelectedIndexChanged( object sender, System.EventArgs e) { BookListArgs bookListArgs = new BookListArgs( ); bookListArgs.bookSelected = ddlBooks.SelectedItem.ToString( ); OnListChanged(bookListArgs); }
Remember to update
OnListChanged
to take the new type of event argument:
protected virtual void OnListChanged(BookListArgs e) { if (ListChanged != null) ListChanged(this, e); }
In VB.NET, you just have to modify the handler for the DropDownList control’s SelectedIndexChanged event:
Private Sub ddlBooks_SelectedIndexChanged(ByVal sender As Object, _ ByVal e As System.EventArgs) _ Handles ddlBooks.SelectedIndexChanged Dim bla As New BookListArgs( ) bla.bookSelected = ddlBooks.SelectedItem.ToString( ) RaiseEvent ListChanged(sender, bla) End Sub
All of the changes noted so far are within the BookList.ascx file. The only change in the page is to the event handler itself. In C#, the code is:
public void Booklist_ListChanged(object sender, UserControl3.BookList.BookListArgs e) { lblMsg.Text = "Selected: " + e.bookSelected; }
In VB.NET, it’s:
Private Sub Booklist1_ListChanged(ByVal sender As System.Object, _ ByVal e As UserControl1VB.Booklist.BookListArgs) _ Handles Booklist1.ListChanged lblMsg.Text = "Selected: " & e.bookSelected End Sub
When you view the web page, it is now able to display the text of the selection, even though the selection event occurs within the user control, as shown in Figure 14-6.
3.138.36.38