Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Pro ASP.NET 2.0 In CSharp 2005 (2005) [eng]

.pdf
Скачиваний:
107
Добавлен:
16.08.2013
Размер:
29.8 Mб
Скачать

738 C H A P T E R 2 1 M E M B E R S H I P

</font>

</span>

</QuestionTemplate>

<SuccessTemplate>

Your password has been sent to your email address <asp:Label ID="EmailLabel" runat="server" />!

</SuccessTemplate>

</asp:PasswordRecovery>

Again, if you use controls with the appropriate ID values and use the appropriate CommandName values for buttons, you don’t have to write any code for the control to work, as in the previous examples where you didn’t use templates. In the previous code, these special controls are in bold. Some of these controls are required for the templates, and others are optional. Table 21-12 lists the controls for PasswordRecovery templates.

Table 21-12. Special Controls for PasswordRecovery Templates

 

 

 

 

Additional

Template

ID

Control Type

Required?

Comments

UsernameTemplate

UserName

System.Web.UI.Web-

Yes

 

 

 

Controls.TextBox

 

 

UsernameTemplate

SubmitButton

All controls that support

No

CommandName

 

 

event bubbling

 

must be set to

 

 

 

 

Submit.

UsernameTemplate

FailureText

System.Web.UI.Web-

No

 

 

 

Controls.Literal

 

 

QuestionTemplate

UserName

System.Web.UI.Web-

No

 

 

 

Controls.Literal

 

 

QuestionTemplate

Question

System.Web.UI.Web-

No

 

 

 

Controls.Literal

 

 

QuestionTemplate

Answer

System.Web.UI.Web-

Yes

 

 

 

Controls.TextBox

 

 

QuestionTemplate

SubmitButton

All controls that support

No

CommandName

 

 

event bubbling

 

must be set to

 

 

 

 

Submit.

QuestionTemplate

FailureText

System.Web.UI.Web-

No

 

 

 

Controls.Literal

 

 

 

 

 

 

 

Again, the submit button can be any control that supports event bubbling and a CommandName property. Typically you can use the controls Button, ImageButton, or LinkButton for this purpose. The CommandName must be set to Submit; otherwise, the command is not recognized by the control (the ID is not evaluated and can therefore be set to any value). The SuccessTemplate doesn’t require any type of control with any special IDs. Therefore, you can add any control you want there; it’s just for displaying the confirmation. In the previous example, it includes a Literal control that should display the e-mail address to which the password has been sent. You can set this Literal control through the SendingEmail event procedure. Again, you can use the FindControl method for finding the control (which is actually a child control of the password control) in the appropriate template, as follows:

protected void PasswordTemplateCtrl_SendingMail(object sender, MailMessageEventArgs e)

{

Label lbl =

C H A P T E R 2 1 M E M B E R S H I P

739

(Label)PasswordTemplateCtrl.SuccessTemplateContainer.FindControl(

"EmailLabel");

lbl.Text = e.Message.To[0].Address;

}

Because the control includes more than one template, you cannot call the FindControl method directly on the PasswordRecovery control instance. You have to select the appropriate template container (UserNameTemplateContainer, QuestionTemplateContainer, or SuccessTemplateContainer). Afterward, you can work with the control as usual. In the previous example, you just

set the text of the label to the first e-mail recipient. Of course, usually for a password recovery, the list has only one mail recipient.

The ChangePassword Control

You can use this control as a standard control for allowing the user to change her password. The control simply queries the user name as well as the old password from the user. Then it requires the user to enter the new password and confirm the new password. If the user is already logged on, the control automatically hides the text field for the user name and uses the name of the authenticated user. You can use the control on a secured page as follows:

<asp:ChangePassword ID="ChangePwdCtrl" runat="server" BorderStyle="groove" BackColor="aliceblue">

<MailDefinition From="pwd@apress.com" Subject="Changes in your profile" Priority="high" />

<TitleTextStyle Font-Bold="true" Font-Underline="true" Font-Names="Verdana" ForeColor="blue" />

</asp:ChangePassword>

Again, the control includes a MailDefinition property with the same settings as the PasswordRecovery control. This is because after the password has been changed successfully, the control automatically can send an e-mail to the user’s e-mail address if a mail server is configured for the web application. As all the other controls, this control is customizable through both properties and styles and a template-based approach. But this time two templates are required when customizing the control:

The ChangePasswordTemplate displays the fields for entering the old user name and password as well as the new password including the password confirmation field.

In the SuccessTemplate the success message is displayed.

The ChangePasswordTemplate requires you to add some special controls with special IDs and CommandName property values. You can find these control ID values and CommandName values in bold in the following code snippet:

<asp:ChangePassword ID="ChangePwdCtrl" runat="server">

<ChangePasswordTemplate>

Old Password: 

<asp:TextBox ID="CurrentPassword" runat="server" TextMode="Password" /><br />

New Password: 

<asp:TextBox ID="NewPassword" runat="server" TextMode="Password" /><br />

Confirmation: 

<asp:TextBox ID="ConfirmNewPassword" runat="server" TextMode="Password" /><br />

<asp:Button ID="ChangePasswordPushButton" CommandName="ChangePassword"

740 C H A P T E R 2 1 M E M B E R S H I P

runat="server" Text="Change Password" /> <asp:Button ID="CancelPushButton" CommandName="Cancel"

runat="server" Text="Cancel" /><br /> <asp:Literal ID="FailureText" runat="server"

EnableViewState="False" />

</ChangePasswordTemplate>

<SuccessTemplate>

Your password has been changed!</td>

<asp:Button ID="ContinuePushButton" CommandName="Continue" runat="server" Text="Continue" />

</SuccessTemplate>

</asp:ChangePassword>

Basically, the text box controls of the ChangePasswordTemplate are all required. The other controls are optional. If you select the ID properties and the CommandName properties for the buttons appropriately, you don’t have to write any additional code.

The CreateUserWizard Control

The CreateUserWizard control is the most powerful control of the login controls. It enables you to create registration pages within a couple of minutes. This control is a wizard control with two

default steps: one for querying general user information and one for displaying a confirmation message. Of course, as the CreateUserWizard inherits from the base Wizard control, you can add as many wizard steps as you want. But when you just add a CreateUserWizard control to your page as follows, the result is really amazing, as shown in Figure 21-16.

<asp:CreateUserWizard ID="RegisterUser" runat="server" BorderStyle="ridge" BackColor="aquamarine">

<TitleTextStyle Font-Bold="true" Font-Names="Verdana" /> <WizardSteps>

<asp:CreateUserWizardStep runat="server"> </asp:CreateUserWizardStep> <asp:CompleteWizardStep runat="server"> </asp:CompleteWizardStep>

</WizardSteps>

</asp:CreateUserWizard>

The default appearance of the control is, again, customizable through properties and styles. The control offers lots of styles, but basically the meaning of the styles is similar to the styles covered for the previous controls. In fact, this control includes the most complete list of styles, as it includes most of the fields presented in the previous controls as well. When you use the CreateUserWizard control as shown previously, you don’t need to perform any special configuration. It automatically uses the configured Membership provider for creating the user, and it includes two steps: the default CreateUserWizardStep that creates controls for gathering the necessary information and the CompleteWizardStep for displaying a confirmation message. Both steps are customizable through styles and properties or through templates. Although you can customize these two steps, you cannot remove them. If you use templates, you are responsible for creating the necessary controls, as follows:

<asp:CreateUserWizard ID="RegisterUser" runat="server" BorderStyle="ridge" BackColor="aquamarine">

<TitleTextStyle Font-Bold="True" Font-Names="Verdana" /> <WizardSteps>

<asp:CreateUserWizardStep runat="server">

<ContentTemplate>

<div align="right">

C H A P T E R 2 1 M E M B E R S H I P

741

<font face="Courier New"> User Name:

<asp:TextBox ID="UserName" runat="server" /><br /> Password:

<asp:TextBox ID="Password" runat="server" TextMode="Password" /><br />

Conform Password:

<asp:TextBox ID="ConfirmPassword" runat="server" TextMode="Password" /><br />

Email:

<asp:TextBox ID="Email" runat="server" /><br /> Security Question:

<asp:TextBox ID="Question" runat="server" /><br /> Security Answer:

<asp:TextBox ID="Answer" runat="server" /><br /> <asp:Literal ID="ErrorMessage" runat="server"

EnableViewState="False" />

</font>

</div>

</ContentTemplate>

</asp:CreateUserWizardStep> <asp:CompleteWizardStep runat="server">

<ContentTemplate>

Your account has been successfully created.</td> <asp:Button ID="ContinueButton" CommandName="Continue"

runat="server" Text="Continue" />

</ContentTemplate>

</asp:CompleteWizardStep>

</WizardSteps>

</asp:CreateUserWizard>

Figure 21-16. A simple CreateUserWizard control

742 C H A P T E R 2 1 M E M B E R S H I P

Because the control is a wizard control, the first step doesn’t require any buttons because a Next button is automatically displayed by the hosting wizard control. Depending on the configuration of the Membership provider, some of the controls are required, and others are not, as listed in Table 21-13.

Table 21-13. Required Controls and Optional Controls

ID

Type

Required?

Comments

UserName

System.Web.UI.Web-

 

 

 

Controls.TextBox

Yes

Always required

Password

System.Web.UI.Web-

 

 

 

Controls.TextBox

Yes

Always required

ConfirmPassword

System.Web.UI.Web-

 

 

 

Controls.TextBox

Yes

Always required

Email

System.Web.UI.Web-

 

 

 

Controls.TextBox

No

Required only if the

 

 

 

RequireEmail property of

 

 

 

the control is set to true

Question

System.Web.UI.Web-

 

 

 

Controls.TextBox

No

Required only if the underlying

 

 

 

Membership provider requires a

 

 

 

password question

Answer

System.Web.UI.Web-

 

 

 

Controls.TextBox

No

Required only if the underlying

 

 

 

Membership provider requires a

 

 

 

password question

ContinueButton

Any control that supports

No

Not required at all, but if

 

bubbling

 

present you need to set the

 

 

 

CommandName to Continue

 

 

 

 

As soon as you start creating additional wizard steps, you will need to catch events and perform some actions within the event procedures. For example, if you collect additional information from the user with the wizard, you will have to store this information somewhere and therefore will need to execute some SQL statements against your database. Table 21-14 lists the events specific to the CreateUserWizard control. The control also inherits all the events you already know from the Wizard control.

Table 21-14. The CreateUserWizard Events

Event

Description

ContinueButtonClick

Raised when the user clicks the Continue button in the last wizard step.

CreatingUser

Raised by the wizard before it creates the new user through the

 

Membership API.

CreatedUser

After the control has been created successfully, the control raises this

 

event.

CreateUserError

If the creation of the user was not successful, this event is raised.

SendingEmail

The control can send an e-mail to the created user if a mail server is

 

configured. This event is raised by the control before the e-mail is sent

 

so that you can modify the contents of the mail message.

SendMailError

If the control was unable to send the message—for example, because

 

the mail server was unavailable—it raises this event.

 

 

C H A P T E R 2 1 M E M B E R S H I P

743

Now you can just add a wizard step for querying additional user information, such as the first name and the last name, and automatically save this information to a custom database table. A valid point might be storing the information in the profile. But when running through the wizard, the user is not authenticated yet; therefore, you cannot store the information into the profile, as this is available for authenticated users only. Therefore, you either have to store it in a custom database table or include a possibility for the user to edit the profile after the registration process.

Furthermore, the CreatedUser event is raised immediately after the CreateUserWizardStep has been completed successfully. Therefore, if you want to save additional data within this event, you have to collect this information in previous steps. For this purpose, it’s sufficient to place other wizard steps prior to the <asp:CreateUserWizardStep> tag. In any other case you have to save the information in one of the other events (for example, the FinishButtonClick event). But because you cannot make sure that the user really runs through the whole wizard and clicks the Finish button, it makes sense to collect all the required information prior to the CreateUserWizardStep and then save any additional information through the CreatedUser event.

<asp:CreateUserWizard ID="RegisterUser" runat="server" BorderStyle="ridge" BackColor="aquamarine" OnCreatedUser="RegisterUser_CreatedUser"

<TitleTextStyle Font-Bold="True" Font-Names="Verdana" /> <WizardSteps>

<asp:WizardStep ID="NameStep" AllowReturn="true"> Firstname:

<asp:TextBox ID="FirstnameText" runat="server" /><br /> Lastname:

<asp:TextBox ID="LastnameText" runat="server" /><br /> Age:

<asp:TextBox ID="AgeText" runat="server" /> </asp:WizardStep>

<asp:CreateUserWizardStep runat="server">

...

</asp:CreateUserWizardStep> <asp:CompleteWizardStep runat="server">

...

</asp:CompleteWizardStep>

</WizardSteps>

</asp:CreateUserWizard>

With the previous wizard step alignment, you now can store additional information in your data store when the CreatedUser event is raised by the control, as follows:

private short _Age;

private string _Firstname, _Lastname;

protected void Page_Load(object sender, EventArgs e)

{

if (!this.IsPostBack)

{

_Age = -1;

_Firstname = _Lastname = string.Empty;

}

}

protected void RegisterUser_CreatedUser(object sender, EventArgs e)

{

// Find the correct wizard step WizardStepBase step = null;

for (int i = 0; i < RegisterUser.WizardSteps.Count; i++)

744 C H A P T E R 2 1 M E M B E R S H I P

{

if (RegisterUser.WizardSteps[i].ID == "NameStep")

{

step = RegisterUser.WizardSteps[i]; break;

}

}

if (step != null)

{

_Firstname = ((TextBox)step.FindControl("FirstnameText")).Text; _Lastname = ((TextBox)step.FindControl("LastnameText")).Text; _Age = short.Parse(((TextBox)step.FindControl("AgeTExt")).Text);

// Store the information

Debug.WriteLine(string.Format("{0} {1} {2}", _Firstname, _Lastname, _Age));

}

}

In the CreatedUser event, the code just looks for the wizard step with the ID set to NameStep. Then it uses the FindControl method several times for getting the controls with the actual content. As soon as you have retrieved the controls, you can access their properties and perform any action you want with them.

In summary, the CreateUserWizard control is a powerful control based on top of the Membership API and is customizable, just as the other login controls that ship with ASP.NET 2.0. With template controls, you have complete flexibility and control over the appearance of the login controls, and the controls still perform lots of work—especially interaction with Membership—for you. And if you still want to perform actions yourself, you can catch several events of the controls.

Using the Membership Class

In the following sections of this chapter, you will learn how you can use the underlying Membership programming interface that is used by all the controls and the whole Membership API infrastructure you just used. You will see that the programming interface is simple. It consists of a class called Membership with a couple of properties and methods and a class called MembershipUser that encapsulates the properties for a single user. The methods of the Membership class perform fundamental operations:

Creating new users

Deleting existing users

Updating existing users

Retrieving lists of users

Retrieving details for one user

Many methods of the Membership class accept an instance of MembershipUser as a parameter or return one or even a collection of MembershipUser instances. For example, by retrieving a user through the Membership.GetUser method, setting properties on this instance, and then passing it to the UpdateUser method of the Membership class, you can simply update user properties. The Membership class and the MembershipUser class both provide the necessary abstraction layer between the actual provider and your application. Everything you do with the Membership class depends on your provider. This means if you exchange the underlying Membership provider, this

C H A P T E R 2 1 M E M B E R S H I P

745

will not affect your application if the implementation of the Membership provider is complete and supports all features propagated by the MembershipProvider base class.

All classes used for the Membership API are defined in the System.Web.Security namespace. The Membership class is just a class with lots of static methods and properties. You will now walk through the different types of tasks you can perform with the Membership class and related classes such as the MembershipUser.

Retrieving Users from the Store

The first task you will do is retrieve a single user and a list of users through the Membership class from the Membership store. For this purpose, you just create a simple page with a GridView control for binding the users to the grid, as follows:

<html xmlns="http://www.w3.org/1999/xhtml" > <head runat="server">

<title>Untitled Page</title> </head>

<body>

<form id="form1" runat="server"> <div>

<asp:GridView ID="UsersGridView" runat="server"

DataKeyNames="UserName"

AutoGenerateColumns="False">

<Columns>

<asp:BoundField DataField="UserName" HeaderText="Username" /> <asp:BoundField DataField="Email" HeaderText="Email" /> <asp:BoundField DataField="CreationDate"

HeaderText="Creation Date" />

</Columns>

</asp:GridView>

</div>

</form>

</body>

</html>

As you can see, the GridView defines the UserName field as DataKeyName. This enables you to access the UserName value of the currently selected user directly through the grid’s SelectedValue property. As most of the methods require the user name for retrieving more details, this is definitely useful. With this page in place, you can now add the following code to the Page_Load event procedure for loading the users from the Membership store and binding them to the grid:

public partial class _Default : System.Web.UI.Page

{

MembershipUserCollection _MyUsers;

protected void Page_Load(object sender, EventArgs e)

{

_MyUsers = Membership.GetAllUsers();

UsersGridView.DataSource = _MyUsers;

if (!this.IsPostBack)

{

UsersGridView.DataBind();

}

}

}

746 C H A P T E R 2 1 M E M B E R S H I P

Figure 21-17 shows the application in action.

Figure 21-17. The custom user management application in action

As you can see, the Membership class includes a GetAllUsers method, which returns an instance of type MembershipUserCollection. You can use this collection just like any other collection. Every entry contains all the properties of a single user. Therefore, if you want to display the details of a selected user, you just need to add a couple of controls for displaying the contents of the selected user in the previously created page, as follows:

Selected User:<br />

<table border="1" bordercolor="blue"> <tr>

<td>User Name:</td>

<td><asp:Label ID="UsernameLabel" runat="server" /></td> </tr>

<tr>

<td>Email:</td>

<td><asp:TextBox ID="EmailText" runat="server" /></td> </tr>

<tr>

<td>Password Question:</td>

<td><asp:Label ID="PwdQuestionLabel" runat="server" /></td> </tr>

<tr>

<td>Last Login Date:</td>

<td><asp:Label ID="LastLoginLabel" runat="server" /></td> </tr>

<tr>

<td>Comment:</td>

<td><asp:TextBox ID="CommentTextBox" runat="server" TextMode="multiline" /></td>

</tr>

<tr>

<td>

<asp:CheckBox ID="IsApprovedCheck" runat="server" Text="Approved" /> </td>

C H A P T E R 2 1 M E M B E R S H I P

747

<td>

<asp:CheckBox ID="IsLockedOutCheck" runat="Server" Text="Locked Out" /> </td>

</tr>

</table>

You can then catch the SelectedIndexChanged event of the previously added GridView control for filling these fields with the appropriate values, as follows:

protected void UsersGridView_SelectedIndexChanged(object sender, EventArgs e)

{

if (UsersGridView.SelectedIndex >= 0)

{

MembershipUser Current = _MyUsers[(string)UsersGridView.SelectedValue];

UsernameLabel.Text = Current.UserName; PwdQuestionLabel.Text = Current.PasswordQuestion;

LastLoginLabel.Text = Current.LastLoginDate.ToShortDateString(); EmailText.Text = Current.Email;

CommentTextBox.Text = Current.Comment; IsApprovedCheck.Checked = Current.IsApproved; IsLockedOutCheck.Checked = Current.IsLockedOut;

}

}

As you can see, the MembershipCollection object requires the user name for accessing users directly. Methods from the Membership class such as GetUser require the user name as well. Therefore, you used the UserName field as content for the DataKeyNames property in the GridView previously. With an instance of the MembershipUser in your hands, you can access the properties of the user as usual.

Updating Users in the Store

Updating a user in the Membership store is nearly as easy as retrieving the user from the store. As soon as you have an instance of MembershipUser in your hands, you can update properties such as the e-mail and comments as usual. Then you just call the UpdateUser method of the Membership class. You can do that by extending the previous code by adding a button to your page and inserting the following code in the button’s Click event-handling routine:

protected void ActionUpdateUser_Click(object sender, EventArgs e)

{

if (UsersGridView.SelectedIndex >= 0)

{

MembershipUser Current = _MyUsers[(string)UsersGridView.SelectedValue];

Current.Email = EmailText.Text;

Current.Comment = CommentText.Text;

Current.IsApproved = IsApprovedCheck.Checked;

Membership.UpdateUser(Current);

// Refresh the grids view UsersGridView.DataBind();

}

}