ASP.NET 2.0 WebPartChrome

In ASP.NET 2.0, a WebPartZoneBase zone defines a region on a Web Parts page that contains WebPart controls. This is the base class for all Web Parts zones that contain WebPart controls. A WebPartZoneBase zone uses a WebPartChrome object to render the WebPart controls that the zone contains. This article discusses the WebPartChrome class and develops a custom WebPartChrome chrome to show you how to develop your own custom WebPartChromes to customize the rendering of WebPart controls contained in a zone.

The best way to understand the WebPartChrome class and its extensible methods is to take a look under the hood of the RenderBody method of the WebPartZoneBase class. The following code listing contains the code for the RenderBody method. As the name implies, this method renders the body of the zone. As this code listing shows, this method iterates through the WebPart controls that the zone contains and calls the RenderWebPart method of the WebPartChrome object to render each enumerated WebPart control:

protected override void RenderBody(HtmlTextWriter writer)
{
  writer.RenderBeginTag(HtmlTextWriterTag.Table);
  foreach (WebPart webPart in WebParts)
  {
    writer.RenderBeginTag(HtmlTextWriterTag.Tr);
    writer.RenderBeginTag(HtmlTextWriterTag.Td);
    WebPartChrome.RenderWebPart(writer, webPart);
    writer.RenderEndTag();
    writer.RenderEndTag();
  }
  writer.RenderEndTag();
}

In other words, WebPartZoneBase delegates the responsibility of rendering its WebPart controls to WebPartChrome. This section first takes you under the hood to help you gain a solid understanding of the methods and properties of the WebPartChrome class. Listing 1 shows these methods and properties. The following sections discuss some of these methods and properties in detail.

Listing 1: The WebPartChrome class
public class WebPartChrome
{
  public WebPartChrome(WebPartZoneBase zone, WebPartManager manager);
  public virtual void
    RenderWebPart(HtmlTextWriter writer, WebPart webPart);
  protected virtual Style CreateWebPartChromeStyle(
    WebPart webPart, PartChromeType chromeType)
  private void RenderTitleBar(HtmlTextWriter writer, WebPart webPart);
  protected virtual WebPartVerbCollection GetWebPartVerbs(
    WebPart webPart);
  protected virtual WebPartVerbCollection FilterWebPartVerbs(
    WebPartVerbCollection verbs, WebPart webPart);
  private bool ShouldRenderVerb(WebPartVerb verb, WebPart webPart);
  protected string GetWebPartChromeClientID(WebPart webPart);
  protected string GetWebPartTitleClientID(WebPart webPart);
  protected virtual void RenderPartContents(HtmlTextWriter writer,
                                            WebPart webPart);
  protected WebPartManager WebPartManager {get;}
  protected WebPartZoneBase Zone {get;}
}

RenderWebPart

Listing 2 contains the implementation of the main parts of the RenderWebPart method of the WebPartChrome class. The boldface portions of this code listing shows some of the extensible methods of WebPartChrome class that you can extend to implement your own custom WebPartChrome, as you'll see later in this article.

Listing 2: The RenderWebPart method
public virtual void RenderWebPart(
  HtmlTextWriter writer, WebPart webPart)
{
  PartChromeType chromeType = this.Zone.GetEffectiveChromeType(webPart);
  Style style = CreateWebPartChromeStyle(webPart, chromeType);
  style.AddAttributesToRender(writer, this.Zone);
  writer.AddAttribute(HtmlTextWriterAttribute.Id, 
                      GetWebPartChromeClientID(webPart));
  writer.RenderBeginTag(HtmlTextWriterTag.Table);
  if ((chromeType == PartChromeType.TitleOnly) || 
      (chromeType == PartChromeType.TitleAndBorder))
  {
    writer.RenderBeginTag(HtmlTextWriterTag.Tr);
    writer.RenderBeginTag(HtmlTextWriterTag.Td);
    RenderTitleBar(writer, webPart);
    writer.RenderEndTag();
    writer.RenderEndTag();
  }
  writer.RenderBeginTag(HtmlTextWriterTag.Tr);
  this.Zone.PartStyle.AddAttributesToRender(writer, this.Zone);
  writer.RenderBeginTag(HtmlTextWriterTag.Td);
  RenderPartContents(writer, webPart)
  writer.RenderEndTag();
  writer.RenderEndTag();
  writer.RenderEndTag();
}

As mentioned, the main responsibility of the RenderWebPart method is to render the WebPart control that WebPartZoneBase passes into it. A WebPart control consists of the following two parts:

  • Chrome: The chrome of a WebPart control is a graphical frame on the control that consists of two parts:
    • Title bar: The title bar includes a title text, a title icon, and a verbs menu
    • Border: The border is the portion of the chrome that frames the WebPart control
  • Content: The content of a WebPart control constitutes its body, that is, the part that the chrome frames

The Web Parts Framework uses the PartChromeType enumeration to specify the type of chrome that the RenderWebPart method must render for the specified WebPart control. The following are the possible values of this enumeration:

  • PartChromeType.Default: The WebPart control inherits the chrome type from the zone that contains the control. The WebZone base class exposes a property of type PartChromeType named PartChromeType that specifies the chrome type for all the WebPart controls that the zone contains.
  • PartChromeType.BorderOnly: RenderWebPart will not render the title bar of the specified WebPart control. It will only render the border.
  • PartChromeType.TitleOnly: RenderWebPart will not render the border that frames the WebPart control. It will only render the title bar.
  • PartChromeType.TitleAndBorder: RenderWebPart will render both the title bar and border of the specified WebPart control.
  • PartChromeType.None: RenderWebPart will render neither the border nor the title bar of the specified WebPart control.

As Listing 2 shows, the RenderWebPart method renders a table that contains two rows where each row contains a single cell. As you'll see shortly, the method renders the title bar within the first row and content or body of the WebPart control within the second row.

Therefore, the first order of business for the RenderWebPart method is to render the table that contains the title bar and body of the specified WebPart control. To render this table, RenderWebPart must render the HTML attributes of the associated <table> HTML element. These HTML attributes mainly consists of the "id" and CSS style attributes. RenderWebPart calls the GetWebPartChromeClientID method of the WebPartChrome to return the client ID value of the specified WebPart control and assigns this value to the id attribute of the <table> HTML element that contains the chrome and body of the WebPart control:

writer.AddAttribute(HtmlTextWriterAttribute.Id,
  GetWebPartChromeClientID(webPart));

What does this mean to you as a control developer? You can call the GetWebPartChromeClientID method to access the value of the id attribute of the containing or main <table> HTML element and use this value in your client-side code to take advantage of the DHTML capabilities of the requesting browser.

RenderWebPart also calls the CreateWebPartChromeStyle method of the WebPartChrome to return the Style object that contains the CSS style attributes of the <table> HTML element that contains the chrome and body of the specified WebPart control.

The default implementation of CreateWebPartChromeStyle builds a Style object based on the chrome type of the specified WebPart control. RenderWebPart calls the GetEffectiveChromeType method of its associated zone to return the chrome type of the WebPart control and passes the chrome type to the CreateWebPartChromeStyle method to return the Style object:

PartChromeType chromeType = this.Zone.GetEffectiveChromeType(webPart);
Style style = CreateWebPartChromeStyle(webPart, chromeType);
style.AddAttributesToRender(writer, this.Zone);

This means two things to you as a developer. First, you can override the CreateWebPartChromeStyle method in your custom WebPartChrome to customize the CSS style attributes of the containing <table> HTML element. Second, you can override the GetEffectiveChromeType method of the WebZone zone to customize the chrome type that is passed into the CreateWebPartChromeType.

Finally, RenderWebPart must render the <table> HTML element itself:

writer.RenderBeginTag(HtmlTextWriterTag.Table);

ASP.NET 2.0 WebPartChrome

Rendering the Title Bar

So far, you've learned how the RenderWebPart method renders the <table> HTML element and its associated HTML attributes. As mentioned, this table contains two rows where each row contains a single cell. RenderWebPart renders the title bar within the first row and the body of the WebPart control within the second row. Listing 3 presents the code that renders the title bar.

Listing 3: The code that renders the title bar
private void RenderTitleBar(HtmlTextWriter writer, WebPart webPart)
{
  writer.RenderBeginTag(HtmlTextWriterTag.Table);
  writer.RenderBeginTag(HtmlTextWriterTag.Tr);
  writer.RenderBeginTag(HtmlTextWriterTag.Td);
  writer.AddAttribute(HtmlTextWriterAttribute.Src, 
                      ResolveClientUrl(webPart.TitleIconImageUrl));
  writer.RenderBeginTag(HtmlTextWriterTag.Img);
  writer.RenderEndTag();
  
  this.Zone.PartTitleStyle.AddAttributesToRender(writer);
  writer.AddAttribute(HtmlTextWriterAttribute.Id,  
                                 GetWebPartTitleClientID(webPart));
  writer.RenderBeginTag(HtmlTextWriterTag.Td);
  writer.WriteEncodedText(webPart.DisplayTitle + " - "
                          + webPart.Subtitle);
  writer.RenderEndTag();
  writer.RenderBeginTag(HtmlTextWriterTag.Td);
  WebPartVerbCollection verbs = GetWebPartVerbs(webPart);
  verbs = FilterWebPartVerbs(verbs, webPart);
  RenderVerbs(writer);
  writer.RenderEndTag();
  writer.RenderEndTag();
  writer.RenderEndTag();
}

As Listing 3 illustrates, the RenderTitleBar method renders a table with a single row that contains three cells. The method first renders the title icon within the first cell:

writer.RenderBeginTag(HtmlTextWriterTag.Td);
writer.AddAttribute(HtmlTextWriterAttribute.Src, 
                    ResolveClientUrl(webPart.TitleIconImageUrl));
writer.RenderBeginTag(HtmlTextWriterTag.Img);
writer.RenderEndTag();

The method then renders the title text within the second cell:

  Zone.PartTitleStyle.AddAttributesToRender(writer);
  writer.AddAttribute(HtmlTextWriterAttribute.Id,  
    GetWebPartTitleClientID(webPart));
  writer.RenderBeginTag(HtmlTextWriterTag.Td);
  writer.WriteEncodedText(webPart.DisplayTitle + " - "
                          + webPart.Subtitle);
  writer.RenderEndTag();

Notice that the method calls the GetWebPartTitleClientID method of the WebPartChrome to return the client ID of the cell that contains the title text. This means you can call the GetWebPartTitleClientID method to access the client ID of this cell and use it in your client-side code to add support for the desired DHTML capabilities. You'll see an example of this later in this article.

Finally, the method renders the verbs menu within the third cell:

writer.RenderBeginTag(HtmlTextWriterTag.Td);
WebPartVerbCollection verbs = GetWebPartVerbs(webPart);
verbs = FilterWebPartVerbs(verbs, webPart);
RenderVerbsMenu(writer, verbs);
writer.RenderEndTag();

To render the verbs menu, the RenderTitleBar method calls the GetWebPartVerbs method of the WebPartChrome to return the verbs for the specified WebPart control. You can override this method in your custom WebPartChrome to filter the verbs that you don't want the current user to see.

Then it calls the FilterWebPartVerbs method of the WebPartChrome to filter the unwanted verbs. It may seem that FilterWebPartVerbs does exactly what GetWebPartVerbs does. Why have two different methods doing the same thing? The answer lies in the fact that there are two types of filtering. Sometimes you filter verbs based on some application-specific logic. For example, as you'll see later, you can filter verbs based on the role the current user is in. To accomplish this kind of filtering you should override the FilterWebPartVerbs method. There are times when you know in advance what verbs shouldn't be rendered. For example, you may decide your users shouldn't be able to close the WebPart controls in your application. To accomplish this kind of filtering you should override the GetWebPartVerbs method.

So far you've learned how the RenderWebPart method renders the title bar within the first row. Now you'll see how this method renders the body of the WebPart control within the second row. As shown in Listing 2, the method delegates the responsibility of rendering the body or content of the WebPart to the RenderPartContents method. Listing 4 presents the code for this method.

Listing 4: The RenderPartContents method
protected virtual void RenderPartContents(
  HtmlTextWriter writer, WebPart webPart)
{
  if (!string.IsNullOrEmpty(webPart.ConnectErrorMessage))
  {
    if (!this.Zone.ErrorStyle.IsEmpty)
      this.Zone.ErrorStyle.AddAttributesToRender(writer, this.Zone);
    
    writer.RenderBeginTag(HtmlTextWriterTag.Div);
    writer.WriteEncodedText(webPart.ConnectErrorMessage);
    writer.RenderEndTag();
  }
  else
    webPart.RenderControl(writer);
}

RenderPartContents first ensures that there're no connection error messages and then calls the RenderControl method of the WebPart control. Chapter 33 "WebPartManager, Web Parts Connections, and Data-Bound WebPart Controls" of the book Professional ASP.NET 2.0 Server Control and Component Development (Wrox, August 2006, ISBN: 0-471-79350-7) covers Web Parts connections in detail. You can override RenderPartContents in your custom WebPartChrome to take control over how the WebPart content is rendered.

PerformPreRender

As shown in Listing 1, the WebPartChrome class doesn't directly or indirectly derive from the Control base class, which means that WebPartChrome isn't a server control. Recall that every server control exposes methods such as OnPreRender, OnLoad, and others that are automatically called when the page enters the associated phases of its life cycle. Because WebPartChrome isn't a server control, none of its methods or properties is automatically called. WebPartChrome relies on its associated WebPartZoneBase, which is a server control, to call its methods when the page enters the associated phases of its life cycle.

The prerender phase plays a significant role when it comes to adding support for DHTML capabilities because the client-side scripts must be registered for rendering in this phase. You can't add these scripts to the RenderWebPart method because this method is called in the rendering phase, not the prerendering phase.

To address this problem, WebPartChrome exposes a method named PerformPreRender. However, because WebPartChrome isn't a server control, its PerformPreRender method will not be automatically called when the page enters its prerender phase. That's why the OnPreRender method of the associated WebPartZoneBase calls the PerformPreRender method of the WebPartChrome as shown in Listing 5.

Listing 5: The OnPreRender method of the WebPartZoneBase control
protected internal override void OnPreRender(EventArgs e)
{
  base.OnPreRender(e);
  WebPartVerbsEventArgs args = new WebPartVerbsEventArgs();
  OnCreateVerbs(args);
  verbs = args1.Verbs;
    WebPartChrome.PerformPreRender();
}

Besides the methods discussed so far, the WebPartChrome also exposes two properties named Zone and WebPartManager, which respectively refer to the WebPartZoneBase and WebPartManager associated with the WebPartChrome object.

The next section builds on what you've learned about WebPartChrome to show you how to develop your own custom WebPartChrome to take control over the rendering of the WebPart controls that the associated zone contains.

ASP.NET 2.0 WebPartChrome

Developing a Custom WebPartChrome

This section develops a custom WebPartChrome named CustomWebPartChrome that derives from the WebPartChrome base class and extends its functionality to add support for the following features:

  • Automatic rendering of a logo in the title bar of every WebPart control that the associated zone contains
  • Using the DHTML feature known as transition to provide an animated effect when the user moves the mouse pointer over the logo

CustomWebPartChrome exposes two new properties named MouseOverImageUrl and MouseOutImageUrl that the page developer must set to the URLs of the images that the CustomWebPartChrome control will render when the user moves the mouse pointer over and out of the control, respectively. In other words, CustomWebPartChrome switches from one image to another when the user moves the mouse pointer over or out of the control.

The revealTrans function is a standard IE function that provides an animated effect when CustomWebPartChrome switches from one image to another. This function takes two arguments. The first argument specifies the duration of the transition or animation. The second argument determines the type of transition or animation.

CustomWebPartChrome exposes an enumeration property of type Transition named Transition. The following code listing shows the possible values of this property. Each value introduces a different animation flavor.

public enum Transition
{
  BoxIn, BoxOut, CircleIn, CircleOut, WipeUp, WipeDown, WipeRight,
  WipeLeft, VerticalBlinds, HorizontalBlinds, CheckerboardAcross,
  CheckerboardDown, RandomDissolve, SplitVerticalIn, SplitVerticalOut,
  SplitHorizontalIn, SplitHorizontalOut, StripsLeftDown, StripsLeftUp,
  StripsRightDown, StripsRightUp, RandomBarsHorizontal,
  RandomBarsVertical, RandomTransition
}
Interested readers are referred to the MSDN Web site for the complete information about the possible values of the transition parameter of the revealTrans method.

As Listing 6 shows, CustomWebPartChrome overrides the PerformPreRender method of its base class to render the client script that supports the DHTML transition feature. This method first iterates through the WebPart controls that the associated zone contains to dynamically generate the client script that supports the transition effect. As Listing 3 shows, the RenderWebPart method of the WebPartChrome assigns the return value of the GetWebPartTitleClientID method to the id HTML attribute of the table cell that contains the title text for the WebPart control. PerformPreRender passes the return value of this method to the getElementById method of the document object to return a reference to the table cell that contains the title text:

js += ("titleBar = document.getElementById('" +
  GetWebPartTitleClientID(webPart) + "');\n");

It then generates the script that returns a reference to the first child element of the table cell:

js += "titleBarFirstChild = titleBar.childNodes[0];\n";

Next, the method generates the script that creates an <img> HTML element and inserts this element as the first child element of the table cell. This ensures the logo will always appear on the top-left corner of the WebPart control:

js += "img = document.createElement('img');\n";
js += "titleBar.insertBefore(img,titleBarFirstChild);\n";

It then generates the script that registers the JavaScript functions MouseOverCallback and MouseOutCallback for the onmouseover and onmouseout client-side events of the logo:

js += "img.onmouseover = MouseOverCallback;\n";
js += "img.onmouseout = MouseOutCallback;\n";

Next, the method generates the script that stores the values of the MouseOutImageUrl and MouseOverImageUrl properties. As you'll see later, the MouseOverCallback and MouseOutCallback JavaScript function uses these values to switch between the two versions of the logo:

js += ("img.srcOut = '" + MouseOutImageUrl + "';\n");
js += ("img.srcOver = '" + MouseOverImageUrl + "';\n");

Then it generates the script that assigns the revealTrans filter to the filter property of the style object and uses the values of the Duration and Transition properties to set the duration and transition parameters of the filter:

js += ("img.style.filter = 'revealTrans(duration=" + 
       Duration.ToString() + ",transition=" + 
       ((int)Transition).ToString() + ")';\n");

Once the script is created, PerformPreRender calls the RegisterStartupScript method to register the script with the containing page. As discussed in Chapter 26 "Developing Ajax-Enabled Controls and Components: Client-Side Functionality" of the book Professional ASP.NET 2.0 Server Control and Component Development (Wrox, August 2006, ISBN: 0-471-79350-7), the containing page renders all the registered scripts when it enters its rendering phase.

Zone.Page.ClientScript.RegisterStartupScript(typeof(CustomWebPartChrome),
  typeof(CustomWebPartChrome).FullName, js);

Finally, the method calls the RegisterClientScriptInclude method to have the containing page render a reference to the CustomWebPartChrome.js script file:

Zone.Page.ClientScript.RegisterClientScriptInclude(
  typeof(CustomWebPartChrome).FullName, "CustomWebPartChrome.js");
Listing 6: The PerformPreRender method
public override void PerformPreRender()
{
  base.PerformPreRender();
  string js =
    "<script language='javascript'>\n\t" +
      "var titleBar;\n" +
      "var titleBarFirstChild;\n" +
      "var img;\n";
  foreach (WebPart webPart in Zone.WebParts)
  {
    js += ("titleBar = document.getElementById('" +
           GetWebPartTitleClientID(webPart) + "');\n");
    js += "titleBarFirstChild = titleBar.childNodes[0];\n";
    js += "img = document.createElement('img');\n";
    js += "titleBar.insertBefore(img,titleBarFirstChild);\n";
    js += ("img.src = '" + MouseOutImageUrl + "';\n");
    js += ("img.srcOut = '" + MouseOutImageUrl + "';\n");
    js += ("img.srcOver = '" + MouseOverImageUrl + "';\n");
    js += ("img.style.filter = 'revealTrans(duration=" +
           Duration.ToString() +
           ",transition=" + ((int)Transition).ToString() + ")';\n");
    js += "img.onmouseover = MouseOverCallback;\n";
    js += "img.onmouseout = MouseOutCallback;\n";
  }
  js += "</script>";
  Zone.Page.ClientScript.RegisterStartupScript(typeof(CustomWebPartChrome),
    typeof(CustomWebPartChrome).FullName, js);

  Zone.Page.ClientScript.RegisterClientScriptInclude(
    typeof(CustomWebPartChrome).FullName, "CustomWebPartChrome.js");
}

Listing 7 shows the JavaScript functions that're called when the user moves the mouse pointer out of and over the control.

Listing 7: The mouse movement JavaScript functions
function MouseOverCallback (img,mouseOverImageUrl)
{ 
  img.filters[0].apply();
  img.src = mouseOverImageUrl;
  img.filters[0].play();   
}
function MouseOutCallback (img,mouseOutImageUrl)
{ 
  img.filters[0].stop();
  img.src = mouseOutImageUrl;
}

The img JavaScript object references the <img> HTML element. This object exposes a collection property named filters that contains all the filters defined in the style attribute of the <img> element. In this case, this collection contains a single item, the transition filter. The filter JavaScript object exposes three important methods, apply, play, and stop.

As Listing 7 shows, the MouseOverCallback calls the apply method of the filter object to take a snapshot of the current image that the <img> element displays:

img.filters[0].apply();

It switches to the new image:

img.src = mouseOverImageUrl;

It then calls the play method of the filter object to start the animation:

img.filters[0].play();

As Listing 7 shows, the MouseOutCallback method calls the stop method of the filter object to stop the animation:

img.filters[0].stop();

It then switches to the new image

img.src = mouseOutImageUrl;

When you develop a custom WebPartChrome, you must also develop a custom WebPartZoneBase that uses your custom WebPartChrome to render its WebPart controls. However, before getting into the implementation of the custom WebPartZoneBase that uses CustomWebPartChrome, you'll need to override one more method of the WebPartChrome base class, RenderWebPart, to ensure that the CustomWebPartChrome is used by your custom WebPartZoneBase.

Listing 8 illustrates the implementation of the RenderWebPart method of the CustomWebPartChrome. As this listing shows, the method ensures the associated zone is of type CustomWebPartZone before it calls the RenderWebPart method of its base class to render the specified WebPart control.

Listing 8: The RenderWebPart method of the CustomWebPartChrome
public override void RenderWebPart(HtmlTextWriter writer,
                                   WebPart webPart)
{
  if (this.Zone.GetType() == typeof(CustomWebPartZone))
    base.RenderWebPart(writer, webPart);
}

CustomWebPartZone

As discussed, RenderBody delegates the responsibility of rendering all the WebPart controls that it contains to a single instance of WebPartChrome. WebPartZoneBase exposes a protected virtual method named CreateWebPartChrome whose default implementation returns an instance of the WebPartChrome class as shown in the following code listing:

protected virtual WebPartChrome CreateWebPartChrome()
{
  return new WebPartChrome(this, base.WebPartManager);
}

CustomWebPartZone is a custom zone that uses CustomWebPartChrome to render its WebPart controls. The CustomWebPartZone custom zone exposes the same four properties that CustomWebPartChrome does: MouseOverImageUrl, MouseOutImageUrl, Duration, and Transition. This custom zone overrides the CreateWebPartChrome method of its base class to instantiate and to return an instance of the CustomWebPartChrome as shown in Listing 9.

Listing 9: The CreateWebPartChrome method
protected override WebPartChrome CreateWebPartChrome()
{
  CustomWebPartChrome webPartChrome =
    new CustomWebPartChrome(this, this.WebPartManager);
  webPartChrome.MouseOutImageUrl = MouseOutImageUrl;
  webPartChrome.MouseOverImageUrl = MouseOverImageUrl;
  webPartChrome.Duration = Duration;
  webPartChrome.Transition = Transition;
  return webPartChrome;
}

CreateWebPartChrome creates a CustomWebPartChrome and assigns the values of the MouseOverImageUrl, MouseOutImageUrl, Duration, and Transition properties of the CustomWebPartZone control to the respective properties of the newly created CustomWebPartChrome object.

Using the CustomWebPartChrome Control

Listing 10 contains a Web page that uses the CustomWebPartZone control and CustomWebPartChrome chrome. Notice that the page sets the values of the MouseOutImageUrl, MouseOverImageUrl, Duration, and Transition properties of the CustomWebPartZone control. Figure 1 shows what the users see on their browsers. Notice that every control added to the CustomWebPartZone automatically contains the logo image in its title bar. When the user moves the mouse pointer over the logo, the WebPart control provides an animated effect.

Listing 10: A Web page that uses the CustomWebPartZone control
<%@ Page Language="C#" %>
<%@ Register TagPrefix="custom" Namespace="CustomComponents" %>
<html xmlns="http://www.w3.org/1999/xhtml">
<body>
  <form id="form1" runat="server">
    <asp:WebPartManager runat="server" ID="mgr" />
    <custom:CustomWebPartZone runat="server" ID="zone1" 
    MouseOutImageUrl="WroxOut.bmp"
    MouseOverImageUrl="WroxOver.bmp" Duration="0.4"
                                     Transition="CircleIn">
      <ZoneTemplate>
        <asp:DropDownList runat="server" ID="ddl" />
        <asp:Calendar runat="server" ID="cal" />
      </ZoneTemplate>
    </custom:CustomWebPartZone>
  </form>
</body>
</html>

[Chrome.jpg]

Figure 1

This article is adapted from Chapter 32 "Developing Custom WebPartZoneBase Controls" of the book Professional ASP.NET 2.0 Server Control and Component Development, by Dr. Shahram Khosravi (Wrox, August 2006, ISBN: 0-471-79350-7). Reprinted with the publisher's permission.



About the Author

Shahram Khosravi

Shahram is a senior software engineer, consultant, author, and instructor specializing in ASP.NET, Web services, .NET technologies, XML technologies, ADO.NET, C#, 3D computer graphics, Human Interface (HI) usability, and design patterns. He has more than 10 years of experience in object-oriented analysis, design, and programming. Shahram has written articles on the .NET Framework, ADO.NET, ASP.NET, and XML technologies for industry leading magazines such as Dr. Dobb's Journal, asp.netPRO magazine, and Microsoft MSDN Online.

Comments

  • There are no comments yet. Be the first to comment!

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Live Event Date: December 11, 2014 @ 1:00 p.m. ET / 10:00 a.m. PT Market pressures to move more quickly and develop innovative applications are forcing organizations to rethink how they develop and release applications. The combination of public clouds and physical back-end infrastructures are a means to get applications out faster. However, these hybrid solutions complicate DevOps adoption, with application delivery pipelines that span across complex hybrid cloud and non-cloud environments. Check out this …

  • On-demand Event Event Date: October 29, 2014 It's well understood how critical version control is for code. However, its importance to DevOps isn't always recognized. The 2014 DevOps Survey of Practice shows that one of the key predictors of DevOps success is putting all production environment artifacts into version control. In this webcast, Gene Kim discusses these survey findings and shares woeful tales of artifact management gone wrong! Gene also shares examples of how high-performing DevOps …

Most Popular Programming Stories

More for Developers

RSS Feeds