Implementing the Start Page of a Web Portal - ASP.NET

The Default.aspx page in this web project is the Start page of the Ajax web portal.(see Figure ).

It contains the following:

• The header which shows a search box

• The Add Stuff area where users can add more widgets

• The tab bar

• The columns

• The footer

The Default.aspx page starts with regular ASP.NET page syntax(see Example).

The Default.aspx page contains almost everything in the Start page

The Default.aspx page contains almost everything in the Start page

Default.aspx: Declarations of external components

The code block in Example does the following:

• Registers the WidgetContainer control, which is the web control for the widget container.

• Adds a reference to the CustomDragDrop assembly, which contains the Custom Drag DropExtender and the Custom Floating Behavior.

• Turns off all caching because the page must be downloaded from the server every time a user visits. If the page is cached, then users will not see the latest content in widgets after making changes on the page.

• Registers the header and footer web controls.

Declarations of external components

Problem: Caching the Default.aspx page on the user’s browser made site fail to work.

Solution: Turn off caching from Default.aspx and serve it directly from the server.

At Pageflakes, we used to cache the Default.aspx page on the user’s browser.But we began receiving complaints that after the page loaded from the browser’s cache, user actions—like clicking a button on a widget or adding a new widget—always failed.We tried our best to locate the problem but could never produce it.

Sometime after this,I was in the U.S. to attend Microsoft’s MIX06.While I was at the hotel and using the hotel’s Internet connection, I encountered the problem myself. Pageflakes was my browser homepage, and it loaded as soon as I started the browser for the first time. But when I tried to use the site, all XML HTTP calls failed.If I did a hard refresh, everything worked fine.

After using the Fiddler Tool for a bit, Welcome page that loaded when you accessed the Internet for the first time to make sure the user is legitimate.

As the Default.aspx page was coming from the cache, there was no request sent to the server and thus the hotel Internet provider could not validate who was using the Internet. So, all XML HTTP calls trying to happened when I tried to access Pageflakes from Starbucks or from airport Wi-Fi zones. So, we turned off caching from Default.aspx and instead made sure that it was always served from our server and the problem disappeared. We stopped receiving complaints too.

The Header Area

The header area displays the logo,the search bars for Live.com and Google, and the Login link.After the header area, there’s a script block that contains some client-side scripts that I will explain later on. These scripts are used to provide some client-side behaviors like calling a web service when dragging and dropping, showing/hiding elements,and so on.Example shows the start of the <body> tag in Default.aspx.

Default.aspx: HTML snippet for the header part

body><form id="form1" runat="server"><!-- Render header first so that user can start typing search criteria

while the huge runtime and other scripts download --><uc1:Header ID="Header1" runat="server" />

Default.aspx: HTML snippet for the header part (continued)

HTML snippet for the header part (continued)

The header control is put before the ScriptManager block. This makes the header emit its HTML right after the body tag. Because browsers render top to bottom, putting the HTML block for the search bar at the top of the page will help the browser render the search bar as soon as the user visits the site.Users typically launch their browsers many times a day for the sole purpose of web searching, so they need to be able to get to the search bar as soon as possible. Otherwise, they will by-pass your page.The HTML block in Example gives us the Google and Live.com search bars(see Figure).

The header needs to be rendered as soon as possible because it contains the search bar,which is usually one of the first tools used

The header needs to be rendered as soon as possible

The Live Search box is pretty cool. It shows the search result in a pop-up right on the page as shown in Figure.

The HTML snippet shown in Example downloads the necessary components for providing the Live.com search functionality right on the page. Just put the snippet anywhere on the page and you have search functionality in that exact spot.Best of all, you don’t need to register on the Live.com web site to get code snippets nor do you need to make web service calls to get search result.Both the UI and the functionality is provided inside the snippet..

Live.com search shows the results on a popup DIV right on the page, which is very handy for web portals because users can stay on the page and see search results

Live.com search shows the results on a popup DIV right on the page

HTML snippet for adding Live.com search bar on any page

HTML snippet for adding Live.com search bar on any page (continued)

Add Stuff Area: The Widget Gallery

This pop-up area appears when the user clicks on the Add Stuff linkThis widget gallery showcases all the available widgets. Users can choose which widget to add to the page from this area.It renders a five-column view of widgets as shown in Figure

Add Stuff area shows where all the widgets in the database are available

Add Stuff area shows where all the widgets in the database are available

Add Stuff area is inside a single UpdatePanel control that contains buttons for adding and hiding widgets, plus the collection (list)of available widgets.The UpdatePanel control makes sure that user actions, such as adding a widget,happen asynchronously, without any postbacks (see Example).

Default.aspx : HTML snippet for the add stuff area (partial)

The update panel contains the Add Stuff and Hide Stuff link buttons. They toggle onthe user’s click. When the user clicks the Add Stuff link, the widget collection isloaded and displayed inside a panel named AddContentPanel. The HTML markup of Add Content Panel is shown in Example.

Default.aspx: AddContentPanel HTML snippet

Default.aspx:AddContentPanel HTML snippet(continued)

The Add Content Panel appears when the user clicks the Add Stuff link.Inside,the DataList named Wid get DataL ist is bound to the widget collection in the data base at runtime from the code.The Add Stuff area fades in when a user clicks the Add Stuff link.An Animation Extender runs the fade-in and fade-out effect (see Example).

Default.aspx:The Animation Extender fades in and out in the Add Stuff area

<ajaxToolkit:AnimationExtender ID="AddContentPanelAnimation" runat="server" TargetControlID="AddContentPanel"><Animations><OnLoad><FadeIn minimumOpacity=".2" /></OnLoad></Animations></ajaxToolkit:AnimationExtender></ContentTemplate></asp:UpdatePanel>

The widget list is loaded by the LoadAddStuff function in Default.aspx.cs. The functionjust binds a list of widgets to the DataList control, and the rendering is done viasimple data binding(see Example).

Default.aspx.cs:LoadAddStuff function


Default.aspx.cs: LoadAddStuff function(continued)


Inside the Load Add Stuff function, you will load the widget list from the data base.List<Wid get> WidgetList is a private variable that loads all the wid gets from the data base only once and then stores in the cache.Example uses the Dash board Facade to load the widget list.

Loading WidgetList once and cache it for the lifetime of the application

WidgetList returns all the wid gets defined in the wid get table. You can’t show all the widgets at once on the data list,so you need to do paging on the Wid get List collection.Paging can be done by using two new function extensions—Skip and Take—that were introduced in LINQ. LINQ adds these two functions on all lists so you can easily do paging on any List<> instance. In the Load Add Stuff function, Skip skips 30 wid gets per page and Take takes 30 widgets from the current page.The result after Skip and Take is another generic List<T> of wid gets of type List<Wid get>,which always contains 30 or fewer items.

When a user clicks on any of the widget links on the widget list,a new instance of the widget is added on the first column of the page.This is handled in the ItemCommand event of the WidgetDataList as shown in Example.

Creating new widget when user clicks on a widget from the widget list

void WidgetDataList_ItemCommand(object source, DataListCommandEventArgs e) { int widgetId = int.Parse( e.CommandArgument.ToString( ) ); DashboardFacade facade = new DashboardFacade(Profile.UserName); WidgetInstance newWidget = facade.AddWidget(widgetId); ... ... }

The Tab Bar

The tab area shown in Figure is inside an Up date Panel because you want the user to add,edit,and delete tabs without refreshing the page.Actions like adding a new tab or editing a tab title do not require the wid get area to be refreshed.However,when the user switches tabs by clicking on another tab, both the tab bar and the wid get area refresh to show the new widgets on the new tab.

Each tab represents a virtual page where the user can put more widgets

Each tab represents a virtual page where the user can put more widgets

Default.aspx: HTML snippet for the tab bar

The tabs are generated at run time inside the <UL> named tabs.Each tab is represented by one <LI>tag containing one Link Button for the Tab title.Example shows how the tab bar is generated from the user’s Page collection.

Creating tabs dynamically

Creating tabs dynamically (continued)

You create one <LI> inside the <UL>for each page.You will notice that I have marked the <UL>tag as runat="server", which means the <UL>tag is now a server control andis available from behind the code.There are two types of <LI> created,one for the active tab (current page) and one for inactive tabs (other pages). Each <LI> contains one Link Button that acts as a click able tab title. When you click the title of the active tab, it allows you to change the tab title.But when you click on an inactive tab, it just switches to that tab and loads the widgets on that page.

At the end of the tab bar, there’s a “new tab” link that adds one new Page object to the user’s Page collection.The new page is represented as a new tab on the tab bar.The “new tab” link is a dynamically created LinkButton, as shown in Example.

Creating a Add New Tab button, which adds a new tab on the tab bar and creates a brand new page for user

When the user clicks on the Add New Tab link, he creates a new Page object with the default setting, makes the new page a current page,reloads the tab bar, and refreshes columns to show the widgets on the new page. The click handler is defined in Example

Handling click on “Add New Tab” link and creating new page for user

void addNewTabLinkButton_Click(object sender, EventArgs e)

The Reload Page function loads the widgets on the current page.A lambda expression—wi => true—is used here.The details of the Reload Page function are explained later in this chapter. But basically, it informs the function that all the widgets on this page will have the FirstVisit flag set to true. This means the widgets will not see that this is a postback, but instead see it as a first visit.Remember,widgets use IWid get Host.Is Post back to determine whether it’s a postback or a first visit. Because tab switching is like a first visit for the widgets on the new tab,you need to tell all widgets that it’s a first visit, not a post back.This makes IWid get Host.IsPost back return false.Details about how tab switching works and how wid gets know whether they should assume that the visit is post back or non post back are explained in the up coming section “Page Switching: Simulating a Non post back Experience.”

When a user clicks on an inactive tab, it changes to active. The wid gets on that page appear on the columns.the new page becomes the user’s current page.So,when the user comes back to the site, he is taken to the last active tab, not the first tab.The user’s last active tab is stored in a UserSetting table. The click handler for inactive tabs is defined in Example .All inactive tabs call the same event handler.

The click handler on the inactive tab makes the tab active when clicked

void PageLinkButton_Click(object sender, EventArgs e)

The user can edit a tab title by clicking on the active tab title. When a user clicks on the title LinkButton, it replaces itself with a text box and a “save” button as shown in Figure

When a user clicks on the My First Page tab, it switches to Edit Mode and allows the user to change the tab title

When a user clicks on the My First Page tab, it switches to Edit Mode and allows the user to change the tab title

The click on the tab is handled by PageTitleEditMode_Click, the server-side event handler, which replaces the title LinkButton with one TextBox and a Button.The event handler is defined in Example.

The event handler when the user switchs to edit mode on a tab

void PageTitleEditMode_Click(object sender, EventArgs e)

Because these two controls are created dynamically, they must have a fixed ID. Otherwise they will fail to postback.

Notice that there’s no click handler for the save Button in Example .So, how does the changed page title get saved? If you use the standard click handler and server-side event approach of ASP.NET, you have to do the following:

• Remember which page is being edited in ViewState

• Render the tabs in SetupTabs function

— Check to see if the tab is being edited before creating a tab

— Render the LinkButton for the title, instead of rendering a text box and a save button

— Hook the click handler on the button

Why a Fixed ID for Dynamically Created Controls Is Needed

When ASP.NET creates a dynamic control, it assigns a sequential ID to it, e.g., ctrl0.The ID is assigned according to the order it is added in the parent control’s Controls collection.This means that if a button is dynamically created on the first visit, it can get an ID like ctrl0.But on a postback, if some other UI elements are created in the same Controls collection before you create the button again, it will get a different ID—say, ctrl1. As a result, when the button tries to restore its state from ViewState, it will look for entries against ID ctrl1, not ctrl0. But on the first visit, the button persisted its state against ctrl0.So, it will fail to restore its state properly.To avoid this, assigna unique ID to each dynamically created control.

This was too much effort. So, let’s try a quick hack. Whenever there’s a postback,you can access the posted controls’ values directly from Request object.So, you can easily get the value of the text box when it is posted back (see Example ).

Code called during the Page_Load event

if(ScriptManager1.IsInAsyncPostBack)

In Example,the value of the Text Box is read directly from the Request object and the page title in the database is changed accordingly.There’s no need to recreate Text Box and Button just to read the page name.Another advantage of this approachis that these functions are never recreated during the post back, so you don’t have toremove them and switch back to view mode after the edit. It’s a quick way to implement in-place edit modules that pop up and go away. At the click of a button, you can create the controls that will serve the in-place edit area and then read the value of the controls directly from the Request object in the Page_Load handler.

However, if you want server-side validation, then you have to go back to complicated approach of always recreating the controls on asynchronous post back because you need to show validation errors, and the controls need to be there to show their invalid content error message.For simplicity, I skipped validation here.

The Widget Area:The Three-Column Widget View

There are the three columns below the tab bar reserved for widgets, which we will refer to as the widget area.The wid get area is a simple three-column HTML table.Each column contains one UpdatePanel and one DropCue, as shown in Example

Default.aspx: Defining the three-column view of the widget area

Wid gets are loaded in side Panels named Left Panel, Middle Panel,and Right Panel.Each widget is dynamically loaded and added inside these panels.If a widget is on the first column,it is added inside Left Panel.The Custom Drag Drop Extender attached to these Panels provides the drag-and-drop support for wid gets, as shown in Figure.

With the three-column view of widgets,each column contains one Up date Panel where the wid gets are loaded

With the three-column view of widgets,each column contains one Up date Panel where the wid gets are loaded

Loading the Start Page

Unlike regular ASP.NET pages,there is no code in the Page_Load event;instead, it is in the Create Child Controls function.ASP.NET calls this function when it needs to create server-side controls on the page.During postback processing on a page, unless all the dynamically created controls are already in place by the time the page loads View State,the events on those dynamic controls will not get fired properly.The Page_Load event happens too late to do this. In Default.aspx,the entire page is constructed dynmically, including the page bar. So, the controls must be created before they load their View State and process postback information, as shown in Example.In the ASP.NET page life cycle,Create Child Controls is the best place to create all dynamic controls that need to load states from ViewState and process postback information.

Dynamically create all controls and widgets on the page

Dynamically create all controls and widgets on the page (continued)

There are three steps involved in loading the full page:
  1. Load the user page setup and user setting (the false passed to the method tells the method not to look for cached information)
  2. Render the tabs that shows user’s pages
  3. Load the widgets on the current page.

The first step is to load the user’s page setup, which includes the following:

• User’s setting,e.g.,current page

• User’s page collection

• Widgets only on the current page

The Load User Page Set up method checks whether this is a first-time user visiting the site.On the very first visit,the user’s page setup is created.On subsequent visits, the user’s existing page setup is loaded, as shown in Example.

for a new user, create the page setup; for an existing user, load the existing page setup

private void LoadUserPageSetup(bool noCache)

the user’s page setup is stored in an ASP.NET cache so that the whole setup is not repeatedly loaded from the database during asynchronous postbacks from widgets.Because there’s no chance of a user’s page setup being changed unless the user adds, edits, or deletes widgets or pages, you can safely cache the whole setup.However, the cache works for single-server and single-process hosting.If you have multiple servers in a web farm or if you have just one server, but Application Pool is configured to use multiple processes in web garden mode,then this cache approach will not work. For example, say two servers in your web farm have cached a user’s page setup. The user now deletes the current page.Say Server A received the postback.The page is deleted from database and the latest page setup is now cached in Server A.But Server B still has the old page setup cached where the deleted page still exists.If the user adds a new widget and the request goes to Server B, it will try to add the widget on the non existent page and fail.

In web garden mode, multiple processes serve the application pool for the web site and suffer from a similar problem as requests from the same user go to different processes.The solution is to use commercial distributed cache solutions, but they are quite expensive. Such solutions give you a cache that is synchronized between all servers.If Server A updates an entry in the cache, it will be synchronized with Servers B, C, and D.When you have a large amount of traffic on the site, you won’t be able to repeatedly load the whole page setup because it will put a lot of stress on the database server.In that case, you will have to go for caching the whole setup for as long as possible and use a distributed cache solution.

The next step is to dynamically create the widgets on the three columns. The function Set up Widgets shown in Example does this difficult job.

Dynamically create widget controls inside column panels

private void SetupWidgets(Func<

// Clear existing widgets if any foreach( Panel panel in columnPanels ) Dynamically create widget controls inside column panels (continued)

The reference to Func<> in the method parameters pertains to new functionality in C#. This function first clears all the widgets from the three Panels.During asynchronous post back, SetupWidgets is called once to recreate the controls and give ASP.NET the exact same control hierarchy so that it can restore ViewState and fire events properly.After the events are processed, if there’s any change on the widget area, such as a widget deleted or added, then Set up Wid gets is called again to render the latest widgets. It’s like calling a Bind method on a DataGrid or DataList again when something changes on the data source.

When Set up Wid gets is called for a second time within the same request, the Panels already have the wid gets.Thus, some wid gets are created twice unless the Panels are cleared.Example uses some C# 3.0 language extensions. We use the OfType<>function to get only the widgets inside the Panels, not any other control.The purpose is to delete only the wid gets and preserve everything else, like the extenders and the drop cue.

Clearing all Wid getContainers from a panel using the OfType<> extension method in C# 3.0

After clearing the panels, one widget container is created for each widget instance.Now comes the Func<> part, which is another very useful extension in C# 3.0.It is called predicate. You can create lambda expressions and pass them as predicateduring a function call.Lambda expressions are shorthand for delegates.Instead of creating a delegate function for simple expressions, you can pass them in a lambda expression form. For example,when a page is loaded for the first time, SetupWidgets is called with a lambda expression, which always returns true for all widget instances.

SetupWidgets( wi => true );

This means, the following statement is always true for all widget instances:

widget.IsFirstLoad = isWidgetFirstLoad(instance);

But you can also specify expressions that return true or false based on a specific condition:

SetupWidgets(wi => wi.ColumnNo == 1);

This will evaluate as true only for those widget instances that are on the middle column (column 1).By doing this,we are telling the function to load middle column widgets as if they are being loaded for the first time. An equivalent C# 2.0 implementation would be as follows:

delegate bool SetupWidgetsDelegate( WidgetInstance wi);


The reason why a lambda expression is used here instead of a simple Boolean true/false is explained in the section “Page Switching: Simulating a Non post back Experience” later in this chapter. The idea is to set wid get. Is First Load = true for some widget instances and set widget.Is First Load = false for some other widget instances. This decision depends on who is calling the Set up Wid gets function.The expression that can take this decision is only known to the function that calls Set up Wid gets.Thus, by using predi cates,the caller can easily pass the expression to Set up Widgets and simplify the logic inside Set up Wid gets.

All rights reserved © 2020 Wisdom IT Services India Pvt. Ltd DMCA.com Protection Status

ASP.NET Topics