Login


A Google DoubleClick Custom Web Control

By Jonathan Wood on 4/19/2011
Language: C#
Technology: ASP.NETWebForms
Platform: Windows
License: CPOL
Views: 9,324
Web Development » Web Services and APIs » General » A Google DoubleClick Custom Web Control

Screenshot of DoubleClick Test Website

Download DoubleClick Control and Test Website Download DoubleClick Control and Test Website

Download DoubleClick Control Source Code Download DoubleClick Control Source Code

Introduction

Website developers wanting to monetize their website might start off with something like the Google AdSense network for displaying ads on their site.

Eventually, they may want to work with advertisers directly and will need the ability to display ads from different sources, for different lengths of time or number of clicks. They'll also need the ability to track that information. One choice for this is Google's DoubleClick for Publishers (DFP). DFP provides a fairly sophisticated platform for serving and tracking ads on any website. For lower-traffic users, this service is free.

As you might suspect, placing DFP ads on a web page requires placing several bits JavaScript on that page. DFP requires two sets of scripts: One is at the location within the HTML where the ad will appear, and the other is within the page's <head> tag.

It's not very complicated. However, there are a number of things to keep straight. Also, there are some additional tracking options that you may want to employ. I recently added DoubleClick ads to one of my websites and decided the easiest way was to write a simple custom web control.

The DoubleClickAd and DoubleClickHeader Web Controls

I came up with two controls: DoubleClickAd and DoubleClickHeader. Not surprisingly, each control corresponds to one of the sets of scripts required by DFP. The DoubleClickAd control goes in your HTML where the ad will appear. And the DoubleClickHeader control goes in the page's <head> tag.

In my case, both controls were placed on a master page. But whether each control is on a content or master page shouldn't matter. As long as the DoubleClickAd control is able to locate the DoubleClickHeader control somewhere within the page's <head> tag, they should work fine.

Listing 1 shows the source code for both controls. Note that they were implemented as custom web control by creating a class that derives from WebControl. A web user control could've been used but custom web controls are much more light-weight, and are preferred when you don't need the ability to design the control in the Visual Studio designer. Since I'm not using any consituent controls, I did not need this ability.

Listing 1: The DoubleClickHeader and DoubleClickAd Custom Web Controls

/// <summary>
/// Renders client script for pages that have one or more DoubleClick ads. This control
/// should be placed within the page's &lt;head&gt; tag.
/// </summary>
[ToolboxData("<{0}:DoubleClickHeader runat=\"server\" PublisherID=\"ca-pub-xxxx\"" +
    " SiteID=\"site1\"></{0}:DoubleClickHeader>")]
public class DoubleClickHeader : WebControl
{
    protected static string _scriptUrl =
        "http://partner.googleadservices.com/gampad/google_service.js";

    /// <summary>
    /// Google DoubleClick for Publishers ID.
    /// </summary>
    public string PublisherID { get; set; }

    /// <summary>
    /// ID of the site. All instances of DoubleClickAd will use this as prefix for ad names.
    /// </summary>
    public string SiteID { get; set; }

    protected List<KeyValuePair<string, string>> _attributes;
    protected List<string> _slots;

    public DoubleClickHeader()
    {
        _slots = new List<string>();
        _attributes = new List<KeyValuePair<string, string>>();
    }

    /// <summary>
    /// Adds a page attribute used for ad selection. Note that values are not
    /// saved across postbacks.
    /// </summary>
    /// <param name="name">Attribute name</param>
    /// <param name="value">Attribute value</param>
    public void AddAttribute(string name, string value)
    {
        _attributes.Add(new KeyValuePair<string, string>(name, value));
    }

    /// <summary>
    /// Adds a DoubleClick ad slot to the header. Normally, you do not call
    /// this method. It is called from instances of DoubleClickAd. Note that
    /// values are not saved across postbacks.
    /// </summary>
    /// <param name="slot">Slot name</param>
    public void AddSlot(string slot)
    {
        // Don't add duplicates
        if (!_slots.Contains(slot))
            _slots.Add(slot);
    }

    // Render control
    protected override void Render(HtmlTextWriter writer)
    {
        // Block 1
        writer.AddAttribute(HtmlTextWriterAttribute.Type, "text/javascript");
        writer.AddAttribute(HtmlTextWriterAttribute.Src, _scriptUrl);
        writer.RenderBeginTag(HtmlTextWriterTag.Script);
        writer.RenderEndTag();
        writer.WriteLine();

        // Block 2
        writer.AddAttribute(HtmlTextWriterAttribute.Type, "text/javascript");
        writer.RenderBeginTag(HtmlTextWriterTag.Script);
        writer.WriteLine("GS_googleAddAdSenseService(\"{0}\");", PublisherID);
        writer.WriteLine("GS_googleEnableAllServices();");
        writer.RenderEndTag();
        writer.WriteLine();

        // Block 3
        if (_attributes.Count > 0)
        {
            writer.AddAttribute(HtmlTextWriterAttribute.Type, "text/javascript");
            writer.RenderBeginTag(HtmlTextWriterTag.Script);
            foreach (var attr in _attributes)
            {
                writer.WriteLine("GA_googleAddAttr(\"{0}\", \"{1}\");", attr.Key, attr.Value);
            }
            writer.RenderEndTag();
            writer.WriteLine();
        }

        // Block 4
        writer.AddAttribute(HtmlTextWriterAttribute.Type, "text/javascript");
        writer.RenderBeginTag(HtmlTextWriterTag.Script);
        foreach (string slot in _slots)
        {
            writer.WriteLine("GA_googleAddSlot(\"{0}\", \"{1}\");", PublisherID, slot);
        }
        writer.RenderEndTag();
        writer.WriteLine();

        // Block 5
        writer.AddAttribute(HtmlTextWriterAttribute.Type, "text/javascript");
        writer.RenderBeginTag(HtmlTextWriterTag.Script);
        writer.WriteLine("GA_googleFetchAds();");
        writer.RenderEndTag();
        writer.WriteLine();
    }
}

/// <summary>
/// Renders a DoubleClick ad.
/// </summary>
[DefaultProperty("Position")]
[ToolboxData("<{0}:DoubleClickAd runat=\"server\" HeaderID=\"DoubleClickHeader1\"" +
    " Position=\"header\" Size=\"468x60\" Type=\"banner\"></{0}:DoubleClickAd>")]
public class DoubleClickAd : WebControl
{
    /// <summary>
    /// ID of the DoubleClickHeader control for this page. Note that value is
    /// not retained across postbacks.
    /// </summary>
    public string HeaderID { get; set; }

    /// <summary>
    /// Position portion of slot name (e.g. siteid_position_size_type).
    /// Must not contain spaces. Note that value is not retained across
    /// postbacks.
    /// </summary>
    public string Position { get; set; }

    /// <summary>
    /// Size portion of slot name (e.g. siteid_position_size_type).
    /// Must not contain spaces.
    /// </summary>
    public string Size { get; set; }

    /// <summary>
    /// Type portion of slot name (e.g. siteid_position_size_type).
    /// Must not contain spaces. Note that value is not retained
    /// across postbacks.
    /// </summary>
    public string Type { get; set; }

    protected string _adName;

    public DoubleClickAd()
    {
        _adName = null;
    }

    // Generate ad name and register with DoubleClickHeader control
    protected override void OnPreRender(EventArgs e)
    {
        DoubleClickHeader hdr = FindControlRecursive(Page.Header, HeaderID) as DoubleClickHeader;
        if (hdr == null)
        {
            string msg = String.Format("Could not find DoubleClickHeader control with ID '{0}'",
                HeaderID);
            throw new Exception(msg);
        }

        // Build slot name (e.g. "site1_header_468x60_banner")
        _adName = String.Format("{0}_{1}_{2}_{3}", hdr.SiteID, Position, Size, Type);
        // Add to header
        hdr.AddSlot(_adName);

        base.OnPreRender(e);
    }

    /// <summary>
    /// Recursively searches for the child control with the given ID.
    /// </summary>
    /// <param name="parent">Parent of controls to be searched</param>
    /// <param name="id">ID to search for</param>
    /// <returns>The matching control, or null</returns>
    public static Control FindControlRecursive(Control parent, string id)
    {
        foreach (Control ctl in parent.Controls)
        {
            if (ctl.ID == id)
                return ctl;

            Control child = FindControlRecursive(ctl, id);
            if (child != null)
                return child;
        }
        return null;
    }

    // Render control
    protected override void Render(HtmlTextWriter writer)
    {
        if (!String.IsNullOrEmpty(_adName))
        {
            writer.WriteLine("<!-- {0} -->", _adName);
            writer.AddAttribute(HtmlTextWriterAttribute.Type, "text/javascript");
            writer.RenderBeginTag(HtmlTextWriterTag.Script);
            writer.Write("GA_googleFillSlot(\"{0}\");", _adName);
            writer.RenderEndTag();
        }
    }
}

Using the DoubleClickAd Control

Using the controls is pretty straight forward. If the compiled version of the controls are visible to your application, you should be able to drag them from the Visual Studio Toolbox to your page.

Listing 2 shows an instance of the DoubleClickAd control placed on an ASP.NET web page. You can place any number of DoubleClickAd controls on each page.

Listing 2: The DoubleClickAd Control Placed on a Page

<cc2:DoubleClickAd ID="DoubleClickAd1" runat="server" 
    HeaderID="DoubleClickHeader1" Position="header" Size="468x60" Type="banner" />

In order for this control to correctly serve DFP ads, you must appropriately initialize the properties of this control. The HeaderID property must be set to the ID of the DoubleClickHeader control on the page. Each page that contains one or more DoubleClickAd controls must also contain a DoubleClickHeader control within the page's <head> tag, and it must have the ID specified in the DoubleClickAd control's HeaderID property. When the ad renders at run time, it will use this ID to locate the header control. The control will throw an exception if a matching header control could not be found.

The remaining properties are used to specify the name of the ad slot. Here, things get slightly more involved. Although you can give your DFP ads almost any name you choose, these controls assume the ad name will be in a form similar to "site1_header_468x60_banner". Here, site1 specifies an ID (defined by you) for your website and is helpful if you have more than one site. Note that the site ID is specified in the DoubleClickHeader control, which I'll discuss in a moment. header specifies the location of the ad slot. As you can probably figure out, 468x60 specifies the size of the ad this slot supports. And, finally, banner specifies the ad type.

The control will construct the ad name from these properties. In addition, you may find it necessary to set some of the properties from code. For example, you may want to display different ads depending on the type of data being displayed on the current page. This is very easy to do. Just ensure that these properties are set in a way that will produce an ad name that matches an ad defined in your DFP account. Note that the controls do not do any work to save settings to ViewState. So any customization done at run time will probably need to be repeated every time the page loads.

This approach should work well for pretty much any site. However, if it doesn't work for you, you'll need to modify the controls' source code to meet your needs.

Using the DoubleClickHeader Control

Listing 3 shows how the DoubleClickHeader control might look when it's placed on a page. Notice that it is placed within the page's <head> tag.

Listing 3: The DoubleClickHeader Control Placed on a Page

<head runat="server">
    <title></title>
    <cc1:DoubleClickHeader ID="DoubleClickHeader1" runat="server" PublisherID="ca-pub-xxxx"
        SiteID="site1" />
</head>

The first thing you need to do is ensure that the ID property is set to the same value of your HeaderID property for your DoubleClickAd controls. Next, you will want to set the PublisherID property to the publisher ID associated with your DFP account.

You'll also want to set the SiteID property. This is used for forming the name of the ad as I discussed previously. If you have problems, run your page and view the source. The generated ad name will appear in some of the macros, which you can compare to the ad names entered in your DFP account.

Additional Features

These controls support some additional functionality as well. DFP gives you the option of specifying additional attributes for your ads, such as whether or not the current user is logged in. This information can then be used to tweak which ads are served, and to also track and compare ad performance between authenticated and unauthenticated users. Additional options are available as well. Refer to your DFP account for what attributes you may want to specify.

Listing 4 shows what your code might look like if you wanted to associate a gender attribute with the current page and the user who is viewing it. Attributes are added using the AddAttribute() method of the DoubleClickHeader control.

Listing 4: Setting a gender attribute in the DoubleClickHeader control

protected void Page_Load(object sender, EventArgs e)
{
    DoubleClickHeader1.AddAttribute("gender", "male");
}

Conclusion

Well, this article has presented a couple of items that may require some consideration. In fact, DFP provides a lot of functionality that may take a little time to get up to speed on. Plan on spending some time defining your DFP inventory and uploading your creatives.

However, once that's done, it shouldn't be a pain to have those ads appear on your website. And the controls I've presented here should make it easier for you when it comes to publishing these ads on ASP.NET pages.

The attached download contains a simple example that uses the controls. Another download contains the controls' source code, in case you'd like to view or modify it.

End-User License

Use of this article and any related source code or other files is governed by the terms and conditions of The Code Project Open License.

Author Information

Jonathan Wood

I'm a software/website developer working out of the greater Salt Lake City area in Utah. I've developed many websites including Black Belt Coder, Insider Articles, and others.