Click here to Skip to main content
Click here to Skip to main content

Multi column layout control

, 7 Jul 2005 Ms-PL
Rate this:
Please Sign up or sign in to vote.
ASP.NET control that allows you to use multi-column layout (known from CSS3) on your web page...

Column control in action!

Image 1. - Three column layout

Introduction

This control allows you to simply and automatically divide your HTML content into multiple columns and present articles in better readable multi-column layout. If you look at the web pages of random newspapers, you'll notice that the width of article text is about 400px. This is because wider texts are less comfortable to read. In paper newspapers, the text of an article is divided into more columns. This technique isn't used on the web pages, because it is more difficult to implement since you need to manually divide articles into more columns. This control makes it possible...

See online demos

Multi-column layout

Multi-column layout is part of CSS 3 specification (More at w3c.org - CSS3 module: Multi-column layout[^]), but since CSS 3 is still only a draft it will take a long time before it will be possible to implement multi-column layout on your web page using CSS 3. If you want to use it now you have to divide every article into columns manually and put these columns in table or floating DIV elements.

This control does all the work automatically, so all you have to do is to put it into your web page, column control then takes its content and divides it into specified number of columns.

<cc:ColumnControl ColumnCount="3" runat="server">
  .. your original long html content ..
</cc:ColumnControl>

How does this control work

There are two main problems with implementing this control. First is how to calculate the estimated height of content and the second is how to divide content into columns without breaking anything, because it can't just split HTML after a specified number of characters. I decided to solve this problem on the server side, but it could be also implemented on the client using JavaScript. I believe that server side is better for this, because it requires quite complex code that would run very slowly on the client and it wouldn't probably work on all clients. On the other hand it is more difficult to estimate the size of HTML elements on the server.

The control contains many features that allow you to specify how the original HTML code should be divided. The control groups HTML tags into the following types: header tags (h1, h2, ...), paragraph tags (p, div, ...), list tags (ul, ol, dl) and list items (li, dd, dt) and others. If control reaches the limit of column while rendering, its behavior depends on the current top level tag.

If breaking occurs in header tag, this header is moved to the beginning of the next column. In the paragraph tag, the control breaks the tag into two parts and the second part is moved to the next column. In list, control waits to the end of the current list item and moves the rest of the list items to the next column. Other tags can't be divided, the control just moves to the end of the tag.

Header and list division

These images show how the control behaves when dividing content. Text highlighted with green color is first and red is second part when calculating where it should be divided. In gray boxes, you can see the result. An interesting fact that you can see from second image is that if you add some more attributes to list (and also to paragraphs) it is automatically copied to the second column, so these attributes aren't lost.

Formatting tags

Automatic division works great, but sometimes you may need to specify something more. For example, you want to define that one section of the document should be divided into three columns, second section shouldn't be divided and third section should be two columns. Sometimes you may also need to insert some additional white space to a specified column (and move content to the following). This is exactly what formatting tags are good for! Formatting tags can be inserted into HTML code as HTML comments, so it is quite simple. Formatting tags can be also very useful when you want to load content form external resources and still be able to change division settings simply.

Image 2. - Usage of formatting tags

The following code shows how you can control dividing behavior. If you enable formatting tags using EnableFormatTags property, control will not divide whole content into columns, but it will only divide sections marked by the cc:section tag. (Image on the right side shows what this code generates.)

<cc:ColumnControl runat="server" 
  EnableFormatTags="true">

  <h1>Header</h1>

  <!--[cc:section cols=2]-->
  <p>First paragraph..</p>
  <p>Second paragraph..</p>
  <!--[/cc:section]-->

  <h2>Small header</h2>

  <!--[cc:section cols=3]-->
  <p>
    This very long paragraph will be 
    divided into three columns...
  </p>
  <!--[/cc:section]-->

</cc:ColumnControl>

Second use for formatting tags is when you need to move some content to the next column and you need to insert additional space to first one. In this case, you can use the cc:space tag as you can see in the following example:

<cc:ColumnControl ColumnCount="3" runat="server">

  <p>First paragraph..</p>
  <!--[cc:space size=50]-->

  <p>Second paragraph..</p>
  <p>Third paragraph..</p>
  
</cc:ColumnControl>

For more advanced examples with formatting tags, see online demo application[^].

Minimal control width

As you can see below (look at image no. 3), the control allows you to specify the minimal width at which multi-column layout is preserved. This prevents the control from displaying more very narrow columns to users with low screen resolution or smaller browser window. This feature is available only when the control renders column layout using div elements. The control contains two different implementations of this feature, one for Internet Explorer and the other for other browsers. I think that IE implementation is very interesting, so I'd like to write a few words about it.

When you use this control to display HTML in two columns, it generates two div elements with CSS style width:50%. When controls width is less than the specified width we need to change it to width:100%. In Mozilla based browsers, the only way to do it is to use JavaScript (for more info look at mozscript.js file in the source code). In IE we can use a CSS expression that looks like this (this style is added to each column):

width:expression(document.getElementById('col_ctrl').offsetWidth<600?'100%':'50%');

This does exactly what we need! If width of an element with ID col_ctrl (this element contains whole control) is more than 600 pixels, width of column is 50% (and content is displayed in multi-column layout), otherwise width is set to 100% and content is displayed in one column.

Control properties

General properties

As you can see from previous examples, you can change column number using ColumnCount property. When you want to control division from content using formatting tags, you can use EnableFormatTags. If formatting tags are enabled ColumnCount is used as default value when you don't specify column count in section tag (<!--[cc:section]-->).

What can be done with MinColumnsWidth

Image 3. - What can be done with MinColumnsWidth property

Generated code

In current HTML, there are two ways of doing multi-column layout. First is using table with specified number of columns and second is using div tags (with CSS styles). Each of this approach has its advantages and disadvantages, so you can decide which one should be used by the RenderMode property. It has the following three possible values:

  • DivFixed - generates columns using div elements. All columns except last one has CSS style float:left to achieve column layout. Each column has CSS class cc_col and it contains another div element with cc_cont class. Last column contains element with cc_last CSS class.
  • TableFixed - generates table with specified number of columns. Each column has CSS class set to cc_col and it also has exactly set width in percents, so column width can't change.
  • TableVariable - Like previous method, generates table and each column has CSS class set to cc_col. Table columns don't have specified width, so width can be adjusted by web browser.

If you use DivFixed render mode, you can also use MinColumnsWidth property to specify minimal width of control at which column layout will be preserved. This means that if you resize control to smaller width, it will display whole content in one column. This feature is demonstrated in the second example[^].

Appearance - column division

Because it is difficult to estimate size of elements, you can help the control by setting properties TagConstants and ElementsSizes. First one can be used to specify ratio between sizes of elements. For example if you expect that one character in pre element has the same size as 10 characters in p element, you can set this property to "pre=10" and control will use this settings for better division. ElementsSizes property allows you to specify how much space is taken by non-pair tags. This is very useful if you want to insert image into document, just use this property (for example "img=500") and control will be able to better estimate size of img tags. Usage of these properties is demonstrated in the third online example page[^].

As described above, the control uses three different approaches to division. You can specify what HTML tags should be considered as header tags using the HeaderTags property (control will never divide tags into multiple columns and it won't be left at the end of column). Next type are lists that are divided only after the end of list item. Tags that are handled as lists can be set using ListTags and list items can be changed using ListItemTags. Last type of tags are ParagraphTags that can be divided into multiple columns. SpaceChars allows you to specify characters that can be used to divide content of paragraph. Control also uses list of all other tags used for text formatting. This list can be modified using PairTags property, but be careful - control expects that all these tags have matching end tag!

Known issues

  • Parser that is used for dividing HTML content passed to the control isn't very smart. It doesn't expect fully valid XHTML (it doesn't try to work with it using XML classes), but it expects that all pair tags have ending tag.
  • I tried to test control as I could, but if you find any example when it generates strange results, please contact me! I'm looking forward to improving it.

Future work and history

  • (7/7/2005) - Control available for ASP.NET 1.1 and ASP.NET 2.0 beta 2.
  • (7/7/2005) - First version of this article published at CodeProject.

License

This article, along with any associated source code and files, is licensed under The Microsoft Public License (Ms-PL)

Share

About the Author

Tomas Petricek

Czech Republic Czech Republic
I live in Prague, the capital city of Czech republic (most of the time Smile | :) ). I've been very interested in functional programming recently and I have a passion for the new Microsoft F# language. I'm writing a book about Functional Programming in the Real World that shows the ideas using examples in C# 3.0 and F#.
 
I've been Microsoft MVP (for C#) since 2004 and I'm one of the most active members of the F# community. I'm a computer science student at Charles University of Prague. My hobbies include photography, fractals and of course many things related to computers (except fixing them). My favorite book writers are Terry Pratchett and Philip K Dick and I like paintings by M. C. Escher.
 
PS: My favorite codeproject icon is Sheep | [baah] .

Comments and Discussions

 
GeneralSource code available online PinmemberGilad Khen30-May-09 17:43 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.141216.1 | Last Updated 7 Jul 2005
Article Copyright 2005 by Tomas Petricek
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid