1. Trang chủ
  2. » Công Nghệ Thông Tin

Pro Server Controls and AJAX Components phần 9 docx

77 334 0

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Tiêu đề Building a Complex Control with Web Service Proxy
Trường học University of Software Engineering
Chuyên ngành Web Services and Controls Development
Thể loại Development Guide
Năm xuất bản 2008
Thành phố Hanoi
Định dạng
Số trang 77
Dung lượng 1,92 MB

Các công cụ chuyển đổi và chỉnh sửa cho tài liệu này

Nội dung

On the first query to Live Search, the Search control will set up the Result control’s DataSource property with an instance of SearchResponse and call its DataBind method to have it bind

Trang 1

Wrapping the Web Service Proxy in a Utility Method

To make it easier to work with the web service proxy, we wrap the creation and invocation

process inside a utility class that abstracts all the details of communicating with the Live Search

web service, as shown in Listing 12-3 This class also hides the work necessary to grab

configu-ration information from the custom configuconfigu-ration section we created earlier in this chapter

Listing 12-3 The SearchUtility.cs Class File

/// Static method for searching Live Search that wraps web service

proxy code for easy invocation

/// </summary>

/// <param name="query">Query to Live Search search web service</param>

/// <param name="sourceRequests">Collection of search settings</param>

/// <returns></returns>

public static LiveSearchService.SearchResponse SearchLiveSearchService(

string query, SourceRequest[] sourceRequests)

{

string LiveSearchLicenseKey = "";

string LiveSearchWebServiceUrl = "";

// get <liveSearchControl> config section from web.config

// for search settings

Trang 2

// if control is instantiated at runtime config section should be present else if (HttpContext.Current != null)

{ throw new Exception(

"ControlsBook2Lib.LiveSearchControls.SearchUtility cannot find <LiveSearchControl> configuration section.");

} EndpointAddress liveSearchAddress = new EndpointAddress(LiveSearchWebServiceUrl);

BasicHttpBinding binding = new BasicHttpBinding();

ChannelFactory<MSNSearchPortType> channelFactory = new ChannelFactory<MSNSearchPortType>(binding, liveSearchAddress);

MSNSearchPortType searchService = channelFactory.CreateChannel();

SearchRequest searchRequest = new SearchRequest();

//Required parameters on SearchRequest searchRequest.Query = query;

searchRequest.AppID = LiveSearchLicenseKey;

searchRequest.CultureInfo = Thread.CurrentThread.CurrentUICulture.Name;

//Optional parameters for SearchRequest

if (sourceRequests != null) searchRequest.Requests = sourceRequests;

//Set mark query word Non-printable character added to highlight query terms //Set DisableHostCollapsing to return all results

searchRequest.Flags = SearchFlags.DisableHostCollapsing | SearchFlags.MarkQueryWords;

//Conduct Search SearchResponse searchResponse = searchService.Search(searchRequest);

return searchResponse;

} }}The SearchUtility class provides a parameter list to its single static SearchLiveSearchService method that accepts the search query string entered by the user and an array of SourceRequest objects This allows the custom server controls to customize what type of search is performed

by setting properties on the SourceRequest objects Consult Tables 12-1 through 12-4 for details

on what settings are available Now that we have covered how to work with the Live Search web service and the configuration architecture, we can focus on the custom server controls

Trang 3

Designing the Control Architecture

At this point, you have an understanding of how to access the Live Search web service, and you

have some code to invoke it to return a set of search results In the next phase of this chapter,

you will learn how to display and interact with results from the Live Search web service

The result set returned by the Live Search web service does not have the tabular structure that traditional data-bound controls such as the Repeater control or the DataGrid control expect

The top-level Live SearchResponse class contains an array of SourceResponse objects that

repre-sents multiple search requests A SourceResponse object contains the overall status information

about the search result, which would more likely be used as a header format The SourceResponse

Results property contains an array of Result instances with the URL data for display in a

repeating item format The control we need to build has to work with the data on these two

separate levels to display it appropriately We achieve this by having templates that bind to

different portions of the data source We discussed how to create templates in Chapter 6

Another major consideration is how to abstract communications with Live Search so that

a developer can quickly add search capabilities to his or her application To provide this ease

of use, we encapsulate the Live Search web service searching inside of our control’s code base

We provide a public data-binding method to load up the control UI from the result set, but the

means to do it are abstracted away from the developer All a developer needs to do is customize

the UI and let the control do the heavy lifting of communicating with Live Search and paging

the result set

The first major architectural decision is to factor out the responsibilities of the control library

Instead of one supercontrol, we factor the functionality into three major controls: Search, Result,

and Pager We also have the ResultItem class, which contains the output templates as a utility

control in support of the Result server control The diagram in Figure 12-5 shows the

break-down of responsibilities

The Search control has the primary responsibility of gathering input from the user and setting up the Result control with the first page of results in a new search We want to separate

Search from Result to allow flexible placement of the Search control’s text boxes The text boxes

can be deployed in separate locations on a web form so as not to constrain the web application

developer from a UI perspective

The Result control handles the display of search results returned by the Live Search web service On the first query to Live Search, the Search control will set up the Result control’s

DataSource property with an instance of SearchResponse and call its DataBind method to have it

bind its templates to the result set This mimics the behavior of data-bound controls discussed

in Chapter 7

The Pager control is the third main control in our control library and is embedded as a child control of the Result control If paging is enabled, the Result control passes the Pager

control the result set so that it calculates the starting index offsets based on page size and

renders page links

Figure 12-6 shows the action that occurs with the Search, Result, and Pager controls on an initial search The end result is a rendered page with embedded links that lets the page post

back to itself to change the view of the search results

Trang 4

Figure 12-5 The architecture of the Live Search controls

Figure 12-6 Controls in action on an initial search

5 Display data for paging

6 Display pages as command hyperlinks

1 2 3

PagerResult

Trang 5

Interacting with the paging features of the Result control is the next bit of functionality we discuss When the links rendered by the Pager control are clicked, the control generates a server-

side event This event is mapped to the Result control, which then handles the process of going

back to the Live Search web service and getting the desired page in the original result set It sets

its own DataSource property and calls DataBind on itself This, in turn, starts the original binding

process to render the new result set Figure 12-7 shows the process graphically when the paging

functionality of our control is exercised

Figure 12-7 Controls in action after the paging link is clicked

Now that we have covered the overall design, we can move on to a more detailed analysis

of the source code in each control, starting in the next section with the Search server control

The Search Control

The Search control takes the input from the user to perform the search query To accomplish

this, we derive the control from the CompositeControl class The Query property exposes the

query string used to search the Live Search web service and is automatically set by the TextBox

control, which is the primary input control for the Search control The Search control does not

expose a starting index property, as it assumes it will be on a one-based scale when it executes

the query RedirectToLiveSearch is a special property that provides the Search control the

capability to ignore the Live Search web service and redirect the web form to the Live Search

web site as if the user had typed in a query at the Live Search site directly

The actual UI for the Search control is built in the composite control fashion of adding child controls from within the following CreateChildControls method The first control added

to the collection is a HyperLink to provide a clickable link back to Live Search as well Note that

the image is the official image made available by the Live Search service The searchTextbox

7 Display pages as hyperlink commands

Pager Result

Trang 6

control is a TextBox control that grabs the input from the user The searchButton control is a Button control that handles posting the page contents from the client back to the web server Several LiteralControl instances are also added to the Controls collection to fill in the HTML spacing between the controls and provide breaks.

protected override void CreateChildControls(){

liveSearchLinkImage = new HyperLink();

Handling the Search

The top of Search.HandleSearch has code that checks an internal Boolean variable named searchHandled to make sure that if both events fire on the same postback, we don’t get dupli-cate searches occurring on the same query value unnecessarily, as shown here:

Trang 7

// check to see if search was handled on this postback

// (this prevents TextChanged and ButtonClicked from

// requesting the same query twice on the Live Search web service)

of the Search control is used to do a dynamic lookup of the correct Result control via the Page

FindControl method Since FindControl is not recursive, we look for the Result control on the

Page as well as at the same nesting level, which is the approach taken by the Net Framework

data-bound control’s DataSourceID property

We also use this control reference to infer the correct value for the PageSize along with the Query property value

if (resControl == null)

resControl = (Result)this.NamingContainer.FindControl(ResultControl);

if (resControl == null)

throw new Exception("Either a Result control is not set on the " +

"Search Control or the Result control is not located on the " +

"Page or at the same nesting level as the Search control.");

SourceRequest[] sourceRequests = new SourceRequest[1];

sourceRequests[0] = new SourceRequest();

sourceRequests[0].Count = resControl.PageSize;

After getting the result data from the web service, we raise an event to any interested parties

The type of this event is named LiveSearchSearched This allows someone to use the Search

control as a data generator and build his or her own custom UI from the result sets We follow

the design pattern for invoking this event through a protected method with On as the prefix to

the search name, OnLiveSearchSearched, as shown here:

OnLiveSearchSearched(new

LiveSearchSearchedEventArgs(searchResponse));

The LiveSearchSearchedEventArgs class wraps the results of a Live Search web service query

We use that event argument’s definition to create a LiveSearchSearched event handler If you go

back to the Search control source code, you can see the code that exposes the LiveSearchSearched

Trang 8

event with this event definition We use the generic EventHandler<T> class to help reduce memory footprint.

After the event is raised so that subscribers receive the Live Search web service search results,

we continue processing in the Search.HandleSearch method to bind data to the Result control: resControl.DataSource = searchResponse;

resControl.DataBind();

We set its DataSource property and call DataBind to have it fill its template structure with HTML that reflects the data of our web service query The final step in the HandleSearch method sets the searchHandled Boolean variable to ensure the control does not fire two Live Search searches if both the TextBox TextChanged and the Button Click events fire on the same postback.Listing 12-4 shows the source code for the Search control

Listing 12-4 The Search.cs Class File

#if LICENSED RsaLicenseData(

"55489e7a-bff5-4b3c-8f21-c43fad861dfa", "<RSAKeyValue><Modulus>mWpgckAepJAp4aU0AvEcGg3TdO+0VXws9LjiSCLpy7aQKD5V7uj49Exh1RtcB6TcuXxm0R6dw75VmKwyoGbvYT6btOIw

QgqbLhci5LjWmWUPEdBRiYsOLD0h2POXs9xTvp4IDTKXYoP8GPDRKzklJuuxCbbUcooESQoYHp9ppbE=</Modulus><Exponent>AQAB</Exponent>

</RSAKeyValue>"

), LicenseProvider(typeof(RsaLicenseProvider)),

#endif DefaultEvent("LiveSearchSearched"),Designer(typeof(SearchDesigner))]

public class Search : CompositeControl {

private const string LiveSearchWebPageUrl = "http://www.live.com";

Trang 9

private const string LiveSearchWebSearchUrl =

private const int SearchTextBoxWidth = 200;

private const bool DefaultFilteringValue = false;

private const bool DefaultRedirectToLiveSearchValue = false;

private bool searchHandled;

private HyperLink liveSearchLinkImage;

private TextBox searchTextBox;

private Button searchButton;

Trang 10

if (disposing) {

//Dispose of additional unmanaged resources here

if (license != null) license.Dispose();

base.Dispose();

} license = null;

_disposed = true;

} }

#endif /// <summary>

/// LiveSearchControls Result control to bind search results to for display /// </summary>

[DescriptionAttribute("Result control to bind search results to for display."), CategoryAttribute("Search")]

virtual public string ResultControl {

get { object control = ViewState["ResultControl"];

if (control == null) return "";

else return (string)control;

} set { ViewState["ResultControl"] = value;

} } /// <summary>

/// Search query string /// </summary>

[DescriptionAttribute("Search query string."), CategoryAttribute("Search")]

Trang 11

virtual public string Query

/// <param name="s">Search button</param>

/// <param name="e">Event arguments</param>

protected void SearchButtonClick(object source, EventArgs e)

{

HandleSearch();

}

Trang 12

private void HandleSearch() {

// check to see if search was handled on this postback // (this prevents TextChanged and ButtonClicked from // requesting the same query twice on the Live Search web service)

if (searchHandled == true) return;

// check for redirect of query processing to Live Search web site

if (RedirectToLiveSearch == true) {

this.Page.Response.Redirect(

LiveSearchWebSearchUrl + "?q=" + HttpContext.Current.Server.UrlEncode(Query), true);

}

if (ResultControl.Length != 0) {

// lookup the Result control we are linked to // and get the PageSize property value Result resControl = (Result)Page.FindControl(ResultControl);

if (resControl == null) resControl = (Result)this.NamingContainer.FindControl(ResultControl);

if (resControl == null) throw new ArgumentException("Either a Result control is not set on the " + "Search Control or the Result control is not located on the " + "Page or at the same nesting level as the Search control.");

SourceRequest[] sourceRequests = new SourceRequest[1];

sourceRequests[0] = new SourceRequest();

Trang 13

// databind search results with the Result control

// we are linked with

/// Protected method for invoking LiveSearchSearched event

/// from within Result control

/// </summary>

/// <param name="lse">Event arguments including search results</param>

protected virtual void OnLiveSearchSearched(LiveSearchSearchedEventArgs e)

Trang 14

/// Overridden to ensure Controls collection is created before external access /// </summary>

public override ControlCollection Controls {

get { EnsureChildControls();

return base.Controls;

} } }}Now that we have covered the search functionality, in the next section, we discuss how the returned results are processed in the Result control

The Result Control

The Result control is the most complex control of the Live Search controls library It is a templated, data-bound control that has the capability to page itself as well as access the web service to update the page range The Result server control takes its cue from the Repeater control we developed in Chapter 7 It provides a robust set of templates: HeaderTemplate, StatusTemplate, ItemTemplate, AlternatingItemTemplate, SeparatorTemplate, and FooterTemplate Each template also has a like-named Style object to modify the HTML that is rendered for style content: HeaderStyle, StatusStyle, ItemStyle, AlternatingItemStyle, SeparatorStyle, and FooterStyle The embedded Pager control has its style properties exposed by a Result class property named PagerStyle

Each template is pushed into an instance of the ResultItem control This is the primary child control of Result, and it provides the means for achieving access to search results from

a template data-binding expression As we mentioned previously, Result offloads most of the paging work to a control class named Pager, which handles offset and range calculations We

Trang 15

stuff the Pager control inside a ResultItem, so that it can page content Figure 12-8 shows the

structural architecture of the Result control, including the portion handed off to the Pager control

Figure 12-8 The ResultItem structure inside the Result control

Notice the square boxes around the search terms “Live”, “Search”, and “Development” in the returned search results in Figure 12-8 The square boxes are nonprintable characters, so

that the developer can highlight search terms in the results if desired In the next section, we

discuss the details behind the ResultItem control class including how to fine-tune the search

results such as adding the ability to highlight search terms

The ResultItem Control

The ResultItem class takes on a structure that is common to containers used as data-bound

templates It has the well-known DataItem property, as well as ItemIndex and ItemType properties to

store the index it occupies in the collection of ResultItem controls aggregated by its parent Result

control The ResultItemType enumeration matches up its usage with the templates and styles from

the Result class as well

Inside this file, we also have a ResultItemEventHandler signature of ResultItem events

These provide interested clients with the capability to receive the creation (ItemCreated) and

Pager

Result ResultItem

Trang 16

data-binding events (ItemDataBound) events of the parent Result control class Listing 12-5 presents the full text listing for the ResultItem control.

Listing 12-5 The ResultItem.cs Class File

using System;

using System.Web.UI.WebControls;

namespace ControlsBook2Lib.CH12.LiveSearchControls{

/// Represents status section below header /// </summary>

Status, /// <summary>

/// Represents search result item output /// </summary>

Item, /// <summary>

/// Represents search result alternating item output /// </summary>

AlternatingItem, /// <summary>

/// Represents separation between search result item or alternating item output /// </summary>

Separator, /// <summary>

/// Represents paging area below search result items /// </summary>

Pager,

Trang 17

private object dataItem;

private ResultItemType itemType;

private int itemIndex;

/// Type of template the ResultItem control represents</param>

/// <param name="dataItem">Data from search query</param>

public ResultItem(int index, ResultItemType type, object dataItem)

Trang 18

} } /// <summary>

/// Type of template the ResultItem control represents /// </summary>

public ResultItemType ItemType {

get { return itemType;

} } } /// <summary>

/// Specialized EventArgs which contains a ResultItem instance /// </summary>

public class ResultItemEventArgs : EventArgs {

private ResultItem item;

/// <summary>

/// Default constructor for ResultItemEventArgs /// </summary>

/// <param name="item">ResultItem control instance</param>

public ResultItemEventArgs(ResultItem item) {

this.item = item;

} /// <summary>

/// ResultItem control instance /// </summary>

public ResultItem Item {

Trang 19

template in use The data-binding expressions reach into a different data objects when they

reference the Container.DataItem property depending on which ResultItem is referenced

The StatusTemplate is bound to the top-level LiveSearchSearchResult class through the DataItem

property The ItemTemplate and AlternatingItemTemplate are alternately bound to each

ResultElement class that makes up the search results HeaderTemplate, FooterTemplate, and

SeparatorTemplate are not bound to any data source and have a null DataItem value

Building the Result Control

To provide a pleasing UI experience out of the box and let the control render something when

it is blank or when it is data bound, we have three primary modes that the Result control

oper-ates in: blank, data binding, and postback The blank mode is used for displaying a UI even

when the user fails to link the control to a data source Data-binding mode is used when a data

source is provided and the user explicitly calls the DataBind method of the control Postback is

the mode the control takes on when it is sent back to the server from a postback event and the

control hydrates its structure from ViewState

The Blank Scenario

The default action of the Result control if you put it on a web form and leave it alone is triggered by

code in its override of the following RenderContents method If a Boolean named searchConducted

is not set, it fires off a call to Result control’s CreateBlankControlHierarchy method:

protected override void RenderContents(HtmlTextWriter writer)

{

// if no search, create a hierarchy with header and

// footer templates only

Trang 20

After the call to the CreateBlankControlHierarchy method, the control next calls PrepareControlHierarchy to ensure all styles are applied to any user-provided templates Last, the control calls the base class method of RenderContents to do its work of iterating through the child controls and rendering them.

If you look at CreateBlankControlHierarchy, you see that it looks for the HeaderTemplate and FooterTemplate templates and creates a ResultItem control to wrap them using the CreateResultItem helper method We examine CreateResultItem in just a bit, but here is CreateBlankControlHierarchy:

private void CreateBlankControlHierarchy(){

if (HeaderTemplate != null) {

ResultItem headerItem = CreateResultItem(-1, ResultItemType.Header, false, null);

items.Add(headerItem);

}

if (FooterTemplate != null) {

ResultItem footer = CreateResultItem(-1, ResultItemType.Footer, false, null);

items.Add(footer);

}}

It adds the ResultItem control to an internal ArrayList collection This is a publicly able collection that is exposed via a top-level Items property on Result, as shown in the following code Notice that we didn’t add the ResultItem controls to the Controls collection of Result in CreateBlankControlHierarchy This is handled by CreateResultItem, along with other things such as data binding and raising item-related events

reach-private Collection<ResultItem> items = new Collection<ResultItem>();

public Collection<ResultItem> Items{

get { return items ; }

}The items collection takes advantage of the List generic type removing the need to create

a custom strongly typed collection

The DataBind Scenario

The next mode of creating child controls inside the Result control class focuses on what happens when the Search control executes a search, sets the DataSource property of the Result control, and invokes its DataBind method, as shown in the following code The first task accomplished is clearing out child controls that might have been put into the collection manually and any

Trang 21

information that might have been persisted to ViewState We also set the all-important

searchConducted Boolean value to true, so the control knows it is not in a blank control situation

public override void DataBind()

true to indicate to CreateControlHierarchy that we are in a data binding scenario We examine

the details of CreateControlHierarchy later in this chapter after we have covered our third mode,

which deals with a rendered Result control rehydrating at the beginning of postback

The Postback Scenario

The Result control’s CreateChildControls method, shown in the following snippet, is called

when a server control needs to build its control structure This could happen as part of the

blank control-building scenario or as part of postback from a client round-trip

override protected void CreateChildControls()

data bound, we do not need to create the control hierarchy We also check to see whether there

is content in the ViewState variable ResultItemCount If this is present, the page is coming back

via postback, and we can call CreateControlHierarchy to have it repopulate the control structure

based on ResultItemControl and have child controls retrieve their former values from ViewState If

the ViewState ResultItemCount variable is not present, we are in a blank control scenario, and

we let the code we have in RenderContents handle the blank mode situation

Creating a Control Hierarchy for Data Binding or Postback

Most of the heavy lifting to build the composite structure of the Result control occurs in the

following CreateControlHierarchy for the data-binding and postback scenarios This code is

typical of your run-of-the-mill data-bound control:

Trang 22

private void CreateControlHierarchy(bool dataBind){

Controls.Clear();

SearchResponse result = null;

// Result items items = new Collection<ResultItem>();

int count = 0;

if (dataBind == true) {

if (DataSource == null) return;

if (temp != null) count = (int)temp;

}

if (HeaderTemplate != null) {

ResultItem headerItem = CreateResultItem(-1,ResultItemType.Header, false, null);

items.Add(headerItem);

} ResultItem statusItem = CreateResultItem(-1, ResultItemType.Status,dataBind, result);

items.Add(statusItem);

// loop through and create ResultItem controls for each of the // result elements from the Live Search web service result ResultItemType itemType = ResultItemType.Item;

for (int i = 0; i < count; i++)

Trang 23

ResultItem item = CreateResultItem(i,

itemType, dataBind, searchResultItem);

// display pager if allowed by user and if results

// are greater than a page in length

if (DisplayPager == true && TotalResultsCount > PageSize)

property of itself DataSource is strongly typed in the implementation of Result to prevent

someone from accidentally assigning a DataSet or other type of collection to it

Trang 24

/// <summary>

/// Data source which takes a SearchResponse to build display

/// </summary>

[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), DefaultValue(null),

Bindable(true), Browsable(false)]

public LiveSearchService.SearchResponse DataSource{

get { return dataSource;

} set { dataSource = value;

}}The CreateControlHierarchy code, when data binding, pulls key parameters from the data source like the number of results, the offset, and the source result set The count variable is set

to the size of the Results array returned to ensure accurate looping in the template creation process If we are not in a data-binding scenario, yet we are creating the control hierarchy, we read ResultItemCount from the ViewState collection to set the count variable Having a count is all we need, because we go through a loop that creates the correct number of ResultItem controls for each of the search results, and the controls then are able to pull their previous information from ViewState

When the code loops through the result set items, it creates the required template for each item and data binds by calling the CreateResultItem method As the result items are processed, the HeaderTemplate, FooterTemplate, and SeparatorTemplates templates are checked for null values, whereas the StatusTemplate and ResultItemTemplate templates are not The reason for this difference is that ResultControl has two prewired template classes as default templates for the StatusTemplate and ItemTemplate if they are not specified by the user You can see this by examining the CreateResultItem method, which is responsible for creating the ResultItem control instances that house the final template content

Once we have looped through each ResultElement of the search query data set, we turn to creating the paging structure Here we call a different method to create the ResultItem instance that houses the paging structure by calling the CreatePagerItem method We also set up the ViewState to remember the count of elements added so we can rehydrate them from ViewState during postback

Creating ResultItem Controls

CreateControlHierarchy offloads most of the work to the CreateResultItem method, as shown

in the following code The CreateResultItem method is the true workhorse of the Result class

It creates the major structures, adds them to the Controls collection, and manages events and data binding

Trang 25

private ResultItem CreateResultItem(int index, ResultItemType itemType,

bool dataBind, object dataItem)

// if no AlternatingItemTemplate, switch to Item type

// and pick up ItemTemplate

Trang 26

case ResultItemType.Separator : selectedTemplate = SeparatorTemplate;

break;

case ResultItemType.Footer : selectedTemplate = FooterTemplate;

if (selectedTemplate != null) {

selectedTemplate.InstantiateIn(item);

} OnItemCreated(new ResultItemEventArgs(item));

Controls.Add(item);

if (dataBind) {

item.DataBind();

OnItemDataBound(new ResultItemEventArgs(item));

} return item;

}The first task for CreateResultItem is to determine what type of ResultItem it is creating using a switch statement The end result of the process is grabbing the correct template from the Result control’s template properties and assigning it to the selectedTemplate method variable For the Status and Item types, it also handles the case of a blank template by instantiating the built-

in default ResultStatusTemplate and ResultItemTemplate classes If the AlternatingItemTemplate property is blank, the Item type ResultItemTemplate default template is used

After template selection, a brand-new ResultItem control is created and is passed its index

in the parent Result control’s Item collection, as well as its type and a potentially valid data source After the ResultItem is minted, it receives the template control content via the Instantiate method of the ITemplate interface

The final step is to fire the required events Once the control is created, we raise an ItemCreated event and add the control to the Controls collection The final step is to call DataBind on the ResultItem if we are in a data-binding scenario, which then raises an ItemDataBound event

Creating the Child Pager Control

The Pager control is added to the Controls collection of the parent Result control in CreatePagerResultItem This special-purpose creation method creates a new ResultItem control and adds a configured Pager control to it as follows:

Trang 27

private ResultItem CreatePagerResultItem()

{

ResultItem item = new ResultItem(-1, ResultItemType.Pager, null);

Pager pager = new Pager();

Pager is configured based on information from the web service query and information that

is exposed by the parent Result control The PageSize property is the number of entries listed

per page that are returned from the Live Search search results PagerBarRange is the number of

pages to display in numeric form at the bottom of the page to go along with the Previous and

Next buttons, if applicable PagerLinkStyle is of type ResultPagerLinkStyle declared as follows It

determines whether text links are displayed or text with DHTML is displayed

public enum ResultPagerLinkStyle

the Pager’s page numbers by inheriting from LinkButton and performing the same steps used

with the HighlightedHyperLink to add DHTML functionality

Notice that we don’t have to explicitly pass LiveSearchService.SearchResponse to Pager

in the CreatePagerResultItem method It has code inside of it to deal with calculating and

displaying the correct page ranges based on the TotalResultsCount, PageNumber, and PageSize

property values

Managing Paging

The Pager control that is part of the child control structure of a paging Result control will raise

the correct command events when a page link is clicked to change the page of results displayed

The command event raised by the Pager control is intercepted by the parent Result control via

the use of the OnBubbleEvent method override:

Trang 28

protected override bool OnBubbleEvent(object source, EventArgs args){

// Handle events raised by children by overriding OnBubbleEvent

// (main purpose is to detect paging events) bool handled = false;

CommandEventArgs cea = args as CommandEventArgs;

// handle Page event by extracting new start index // and calling HandleSearch method, which does the // work of rebinding this control to the results // from the web service

if (cea.CommandName == "Page") {

StartIndex = Convert.ToInt32(cea.CommandArgument);

HandleSearch();

} return handled;

}The OnBubbleEvent implementation in Result grabs the index of the new page to display with the Result control and then calls HandleSearch, which actually talks to Live Search HandleSearch is similar to the method of the same name in the Search control, except that it doesn’t have to look up a Result control; it simply sets the DataSource and calls DataBind on itself

Styling the Result Control

After all of the child controls are created, either by CreateBlankControlHierarchy or CreateControlHierarchy, the styles exposed by the Result control are applied This is handled

in the RenderContents override discussed earlier At the end of RenderContents, the code invokes PrepareControlHierarchy to make this happen It loops through all the ResultItem controls and applies the appropriate Style object if the style was set on the Result control, as shown here:

protected void PrepareControlHierarchy(){

// apply all the appropriate style attributes // to the items in the result output

foreach (ResultItem item in this.Items) {

if (item.ItemType == ResultItemType.Header) {

if (HeaderStyle != null) item.ApplyStyle(HeaderStyle);

} else if (item.ItemType == ResultItemType.Status) {

Trang 29

Because we know there is only one instance of the Pager server control stored as a ResultItem,

we apply PagerStyle directly to this instance, as shown in the preceding code Listing 12-6

shows the full source for the Result control

Listing 12-6 The Result.cs Class File

Trang 30

using ControlsBook2Lib.CH12.LiveSearchControls.Design;

using LiveSearchService;

namespace ControlsBook2Lib.CH12.LiveSearchControls{

/// Render pager DHTML for page link buttons in pager

/// Not implemented but a place holder for extension /// </summary>

TextWithDHTML }

#if LICENSED RsaLicenseData(

"55489e7a-bff5-4b3c-8f21-c43fad861dfa",

"<RSAKeyValue><Modulus>mWpgckAepJAp4aU0AvEcGg3TdO+0VXws9LjiSCLpy7aQKD5V7uj49Exh1RtcB6TcuXxm0R6dw75VmKwyoGbvYT6btOIwQgqbLhci5LjWmWUPEdBRiYsOLD0h2POXs9xTvp4IDTKXYoP8GPDRKzklJuuxCbbUcooESQoYHp9ppbE=</Modulus><Exponent>AQAB

</Exponent></RSAKeyValue>"

), LicenseProvider(typeof(RsaLicenseProvider)),

#endif DefaultEvent("LiveSearchSearched") ]

public class Result : CompositeControl {

// constants private const int defaultPageSize = 10;

private const int defaultPagerBarRange = 4;

Trang 31

private const int defaultPageNumber = 1;

// style property fields

private Style headerStyle;

private Style statusStyle;

private Style itemStyle;

private Style alternatingItemStyle;

private Style separatorStyle;

private Style pagerStyle;

private Style footerStyle;

private ResultPagerLinkStyle pagerLinkStyle =

ResultPagerLinkStyle.TextWithDHTML;

// Template property fields

private ITemplate headerTemplate;

private ITemplate statusTemplate;

private ITemplate itemTemplate;

private ITemplate alternatingItemTemplate;

private ITemplate separatorTemplate;

private ITemplate footerTemplate;

private bool searchConducted;

private SearchResponse dataSource;

private Collection<ResultItem> items = new Collection<ResultItem>();

Trang 32

get { return HtmlTextWriterTag.Div;

} } #region Dispose pattern

#if LICENSED private bool _disposed;

/// You must override Dispose for controls derived from the License clsas /// </summary>

protected virtual void Dispose(bool disposing) {

if (!_disposed) {

if (disposing) {

//Dispose of additional unmanaged resources here

if (license != null) license.Dispose();

base.Dispose();

} license = null;

_disposed = true;

} }

#endif #endregion #region Search properties /// <summary>

/// Number of search results returned with query and displayed on page /// </summary>

[Description(

"Number of search results returned with query and displayed on page."), Category("Search"), DefaultValue(defaultPageSize)]

Trang 33

virtual public int PageSize

Trang 34

if (count == null) return 0;

else return (int)count;

} } /// <summary>

/// Search query string

if (query == null) return string.Empty;

else return (string)query;

} set { ViewState["Query"] = value;

} } #endregion #region Appearance properties /// <summary>

/// Display paging links at bottom of search results

if (pager == null) return true;

else return (bool)pager;

}

Trang 36

#region Miscellaneous properties /// <summary>

/// Data source which takes a SearchResponse to build display

/// </summary>

[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), DefaultValue(null),

Bindable(true), Browsable(false)]

public LiveSearchService.SearchResponse DataSource {

get { return dataSource;

} set { dataSource = value;

} } /// <summary>

/// Collection of child ResultItem controls /// </summary>

[Browsable(false)]

public Collection<ResultItem> Items {

get { return items;

} } #endregion #region Style properties /// <summary>

/// The style to be applied to header template

/// </summary>

[Category("Style"), Description("The style to be applied to header template."),DesignerSerializationVisibility(DesignerSerializationVisibility.Content), NotifyParentProperty(true),

PersistenceMode(PersistenceMode.InnerProperty), ]

public virtual Style HeaderStyle {

Trang 38

public virtual Style ItemStyle {

get {

if (itemStyle == null) {

itemStyle = new Style();

if (IsTrackingViewState) ((IStateManager)itemStyle).TrackViewState();

} return itemStyle;

} } /// <summary>

/// The style to be applied to alternate item template

/// </summary>

[Category("Style"),Description("The style to be applied to alternate item template."),DesignerSerializationVisibility(DesignerSerializationVisibility.Content),NotifyParentProperty(true),

PersistenceMode(PersistenceMode.InnerProperty),]

public virtual Style AlternatingItemStyle {

get {

if (alternatingItemStyle == null) {

alternatingItemStyle = new Style();

if (IsTrackingViewState) ((IStateManager)alternatingItemStyle).TrackViewState();

} return alternatingItemStyle;

} } /// <summary>

/// The style to be applied to the separator template /// </summary>

[Category("Style"), Description("The style to be applied to the separator template."),DesignerSerializationVisibility(DesignerSerializationVisibility.Content), NotifyParentProperty(true),

PersistenceMode(PersistenceMode.InnerProperty), ]

Ngày đăng: 12/08/2014, 23:20

TỪ KHÓA LIÊN QUAN