tags:

views:

198

answers:

3

I have read this excellent Multi-column list article, as well as this question on SO. I've come to the conclusion that there is no cross-browser way to turn a long unordered list into n columns of equal length. So far, I have been reduced to multiple-ul solutions like this:

//Three columns.
string col1 = string.Empty;
string col2 = string.Empty;
string col3 = string.Empty;
int currItem = 0;
int collectionCount = myItemCollection.Count;

foreach item in myItemCollection {
  currItem++;
  if (currItem < collectionCount * .33)
  {
    col1 = col1 + item.someProperty
  } 
  else if (currItem < collectionCount * .67)  
  {
    col2 = col2 + item.someProperty
  } 
  else
  {
    col3 = col3 + item.someProperty
  }
}

string allColumns = @"<ul>" + col1 + "</ul><ul>"
                      col2 + "</ul><ul>" + col3 + "</ul>";

Response.Write(allColumns);

Is there a simpler way to separate my list into groups of three, or better yet, to simply write the appropriate closing/starting ul tags when an element is the last item in a "third"?

A: 

If it is an unordered list, you can just float the <li> left and give it a width of a little bit less than (100 / n)%

jeroen
+1  A: 

This is how I personally would choose to implement it.

const int numColumns = 3;
const int numColumns = 3;
var columnLength = (int)Math.Ceiling((double)myItemCollection.Count / 3);

for (int i = 0; i < myItemCollection.Count; i++)
{
    if (i % columnLength == 0)
    {
        if (i > 0)
            Response.Write("</ul>");
        Response.Write("<ul>");
    }
    Response.Write(myItemCollection[i].SomeProperty);
}

if (i % columnLength == 0)
    Response.Write("</ul>");

You avoid string concatenation altogether (there's really no need when you're just writing to a stream, and if you weren't you'd want to xse StringBuilder) as well as those nasty floating point operations which could potentially cause inaccuracies for long lists (at the very least they're unnecesary).

Anyway, hope that helps...

Noldorin
Excellent. With some minor cleanup this code worked absolutely perfectly. Exactly what I was hoping for.
Peter J
Glad it did the trick for you... (Also let me know if it contained any bugs so I can edit it, otherwise I will assume that you simply made small modifications for your own specific uses.)
Noldorin
+1  A: 

This is too late, but Response.Write will output in an unpredictable place, most likely ahead of all other output.

The "proper" code should be building controls on CreateChildren, Page_Load or any other place:

List<string> items = new List<string>() { "aaa", "bbb", "ccc", "ddd", "eee" };
int colSize = (int)Math.Ceiling(items.Count / 3.0);

HtmlGenericControl ul = null;
for (int i = 0; i < items.Count; i++)
{
 if (i % colSize == 0)
 {
  ul = new HtmlGenericControl("ul");
  Page.Form.Controls.Add(ul);
 }

 HtmlGenericControl li = new HtmlGenericControl("li");
 li.InnerText = items[i];
 ul.Controls.Add(li);
}

This way you don't need to be concerned about rendering and tracking opening/closing tags.

Ruslan