Bugfree.dk – Ronnie Holm's blog

Not anti-anything, just pro-quality

Archive for July, 2008

How to modify the enclosing tag of a web control

Posted by Ronnie on 30th July 2008

In this post I’ll rework a piece of code that changes the enclosing tag of a web control from a span to a div. A substitution of particular value with SharePoint, where adding web controls is a common way to extend the out of box experience. With SharePoint, and Asp.Net in general, a page is typically composed of reusable controls, each responsible for rendering its part of the page.

To showcase anti-patterns of API usage, intermediate code is included, i.e., code that may render the correct tags, although not necessarily in the true spirit of the .Net framework.

To set the stage, imagine writing a web control that inherits from ASP.Net WebControl. The goal is then to modify the behavior of the control in such a way that it (1) encloses its content in a div tag and (2) adds a CSS class for styling, and (3) can be added to a page like so:

   <MyControls:MyControl CssClass="myCssClass" runat="server" />

After making its way through the page rendering pipeline, the control tag gets transformed to:

   <div class="myCssClass">
      Hello World
   </div>

Within the control, a straight-forward way of achieving the intended output may be to hand craft the html and render it in RenderContents of WebControl. It almost goes without saying that rendering html by hand is but only a starting point when working with .Net. The next progression from there may be to override RenderBeginTag and RenderEndTag of WebControl:

   // version 1
   public class MyControl : WebControl {
      public override void RenderBeginTag(HtmlTextWriter w) {
         w.RenderBeginTag(string.Format("div class=\"{0}\"", CssClass));
      }

      public override void RenderEndTag(HtmlTextWriter w) {
         w.RenderEndTag();
      }

      protected override void RenderContents(HtmlTextWriter w) {
         w.Write("Hello World");
      }
   }

Resulting in this piece of html:

   <div class="myCssClass">
      Hello World
   </div class="myCssClass">

Clearly, RenderBeginTag and RenderEndTag of HtmlTextWriter don’t know how to separate tag from attribute. Nonetheless, the code reveals the stack based approach taken by the Render-pair of methods.

Gleaning over the documentation of HtmlTextWriter, however, I encountered the AddAttribute method for setting attributes on a tag. Inside HtmlTextWriter, what AddAttribute does is add attributes and associated values to an array that gets rendered during the next call to RenderBeginTag. So besides introducing strongly typed attributes and tags, the next version contains a modified RenderBeginTag:

   // version 2
   public override void RenderBeginTag(HtmlTextWriter w) {
      w.AddAttribute(HtmlTextWriterAttribute.Class, CssClass);
      w.RenderBeginTag(HtmlTextWriterTag.Div);
   }

Now the control renders as expected, although the code still has a certain bad smell to it. WebControl ought to know how to properly render its begin and end tag. After all it’s WebControl that exposes, among others, the CssClass property. So taking another dive into the documentation, I found the virtual, read-only TagKey property of WebControl. Strangely named as it may be, overriding TagKey makes WebControl call our override as it renders the control, adding in optional attributes, such as CssClass. Implementing the property also turns RenderBeginTag and RenderEndTag obsolete:

   // version 3
   protected override HtmlTextWriterTag TagKey {
      get {
         return HtmlTextWriterTag.Div;
      }
   }

As an added bonus, changing the enclosing tag can also be done calling the WebControl’s constructor, passing in the desired tag:

   // version  4
   public class MyControl : WebControl {
      public MyControl() : base(HtmlTextWriterTag.Div) { }

      protected override void RenderContents(HtmlTextWriter w) {
         w.Write("Hello World");
      }
   }

As it turns out, calling the constructor is how WebControl derived classes get to use span in the first place. The default WebControl constructor passes control onto another constructor that sets the value of what gets returned by the base class TagKey implementation. In essence, overriding TagKey makes it a player in the template design pattern:

   // .Net framework code
   protected WebControl() : this(HtmlTextWriterTag.Span) { }

   public WebControl(HtmlTextWriterTag tag) {
      tagKey = tag;
   }

Also worth noting is that for controls deriving from WebControl derived classes, such as CompositeControl, the constructor cannot be used to set the enclosing tag. The constructor exposed by CompositeControl and friends doesn’t call up the constructor chain passing in the tag, making TagKey the choice to go.

Finally, deep down the inhertitance chain below WebControl lives WebPart, a type of control very popular with SharePoint. Interestingly enough, WebPart defaults to using div as its enclosing tag, because walking up the chain, we come across the constructor of Panel:

   // .Net framework code
   public Panel() : base(HtmlTextWriterTag.Div) { }

So, using Panel provides another way to implement a web control that owns a distinct part of a page.

  • Share/Bookmark

Tags: , ,
Posted in .Net, SharePoint | No Comments »

Code based, dynamic CAML query composition

Posted by Ronnie on 3rd July 2008

Today I faced an interesting challenge while querying list items in SharePoint. On a page I have a control that displays list items based on the terms used to tag the page, i.e., the content type on which the page is based contains a multi-valued field that holds a subset of predefined tags. Similarly, for items in a SharePoint list, each item is tagged using a subset of the same predefined tags. The responsibility of the control is then to (1) read the tags associated with the page and (2) query the tags of the list items in an OR-wise fashion. The (3) outcome is then displayed by the control as a set of context specific items.

To decide on the items to display, an initial approach might be to go through the list one item at a time, looking for matching tags. But working with large lists in SharePoint this approach is not recommended. Alternatively, the predicate against which each item in the list is evaluated could take on the form of a CAML query. Looking for items matching a single tag is then easily expressed:

   <Where>
      <Eq>
         <FieldRef Name='MyField' />
         <Value Type='LookupMulti'>tag1</Value>
      </Eq>
   </Where>

Next, consider a query with two tags OR’ed together. Although more verbose, the query is still fairly straightforward to compose on the fly:

   <Where>
      <Or>
         <Eq>
            <FieldRef Name='MyField' />
            <Value Type='LookupMulti'>tag1</Value>
         </Eq>
         <Eq>
            <FieldRef Name='MyField' />
            <Value Type='LookupMulti'>tag2</Value>
         </Eq>
      </Or>
   </Where>

However, for CAML queries where more than two tags are OR’ed together, the binary nature of the OR operator works against us. Where real programming languages have their parsers automatically transform an expression like (a || b || c) into ((a || b) || c), the CAML query parser applies no such transform — In a sense CAML is more like an XML serialized, abstract syntax tree than a query language intended for direct use:

    <Where>
      <Or>
         <Or>
            <Eq>
               <FieldRef Name='MyField' />
               <Value Type='LookupMulti'>tag1</Value>
            </Eq>
            <Eq>
               <FieldRef Name='MyField' />
               <Value Type='LookupMulti'>tag2</Value>
            </Eq>
         </Or>
         <Eq>
            <FieldRef Name='MyField' />
            <Value Type='LookupMulti'>tag3</Value>
         </Eq>
      </Or>
   </Where>

So how do we go about composing a query that OR together n tags (not to mention a mix of ORs and ANDs)? One approach might be using the U2U CAML Query Builder. The tool delivers a UI for formulating queries, but also exposes its CAML query builder API as a .Net assembly. Unfortunately, documentation on how to use the builder is sparse. Nonetheless, I went for the case of composing a query that OR together three tags:

   string ComposeCamlQuery() {
      Builder b = new Builder(CamlTypes.GetListItems);
      b.AddViewField("Title");
      b.AddViewField("MyField");

      bool addCombinerNode;
      foreach (string tag in new string[] { "tag1", "tag2", "tag3" })
         b.AddWhereField("MyField", tag, "LookupMulti",
                         "Or", out addCombinerNode);
      return b.CamlDocument.InnerXml;
   }

The outcome is a rather strange looking CAML query with extra <And></And> and missing <Eq></Eq> tags. Funny thing is that this type of the query can be correctly composed through the UI, so most likely I’m not using the API correctly:

   <Where>
      <And>
         <And>
            <Or>
               <FieldRef Name="MyField" />
               <Value Type="LookupMulti">tag1</Value>
            </Or>
            <Or>
               <FieldRef Name="MyField" />
               <Value Type="LookupMulti">tag2</Value>
            </Or>
         </And>
         <Or>
            <FieldRef Name="MyField" />
            <Value Type="LookupMulti">tag3</Value>
         </Or>
      </And>
   </Where>

I eventually abandoned the idea of using the CAML Query Builder API. Instead, a colleague pointed me to Waldek Mastykarz’s post on generating dynamic CAML queries. As long as all tags are to be either AND’ed or OR’ed together, Mastykarz provides an elegant solution, which I modified to be recursive, more generic, and read like an induction proof (at a negligible performance cost):

   ComposeCamlQuery(new[] { "tag1", "tag2", "tag3" },
                    "Or",
                    "<Where>{0}</Where>",
                    @"<Eq>
                       <FieldRef Name='MyField' />
                       <Value Type='LookupMulti'>{0}</Value>
                      </Eq>");

   string ComposeCamlQuery(IList<string> ops, string relOp,
                           string query, string leaf) {
      return ops.Count == 1
         ? string.Format(query,
                         string.Format(leaf, ops[0]))
         : ComposeCamlQuery(ops.Skip(1).ToList(),
                            relOp,
                            string.Format(query,
                               string.Format("<{0}>{1}{{0}}</{0}>",
                               relOp,
                               string.Format(leaf, ops[0]))),
                            leaf);
   }

Obviously, more complex code could be written for a mix of AND’s and OR’s. Before doing so, however, you probably want to evaluate other approaches for filtering list items:

  1. Formulate a simpler, too general query and post-process the result
  2. Formulate smaller queries and optionally merge the results and do post-processing
  3. Use the SharePoint Search API

I also considered such alternatives as Linq to SharePoint or Caml.net, but although the first holds a lot of promise, it isn’t ready for prime time. As for the latter, its focus is more on composing type-safe queries than dynamic ones, so it’s more of a supplement to the first two alternatives.

As for SharePoint Search, the downside is that search has to be configured and that the list must have been indexed for the outcome to be accurate. Depending on the frequency with which the list is modified, the time between incremental crawls, and the context in which the results are displayed, the search approach may or may not be the solution you’re looking for.

Update, July 19: Added recursive code solution.

  • Share/Bookmark

Tags: ,
Posted in SharePoint | 2 Comments »