mike's web log

 

Blog Search


(Supports AND)

 

Google Ads

 

Technorati

 

Feed

Subscribe to the RSS feed for this blog.

See this post for info on full versus truncated feeds.

 

Quote

Anybody who reads the newspaper can easily look at the high-tech industry and see that stupidity is like beer at an NFL football game: Half the people have got plenty of it and they keep spilling it on the other half.

Eric Sink



 

Navigation






<September 2010>
SMTWTFS
2930311234
567891011
12131415161718
19202122232425
262728293012
3456789


 

25 Most-Visited Entries

 

Categories

  RSS
  RSS
  RSS
  RSS
  RSS
  RSS
  RSS
  RSS
  RSS
  RSS
  RSS
  RSS
  RSS
  RSS
  RSS
  RSS
  RSS
  RSS
  RSS
  RSS
  RSS
  RSS
  RSS
 

Blogs I Read

 

Contact

Email me
 

Blog Statistics

Dates
First entry - 6/27/2003
Most recent entry - 8/26/2010

Totals
Posts - 2109
Comments - 2170
Hits - 1,140,239

Averages
Entries/day - 0.80
Comments/entry - 1.03
Hits/day - 434

Update every 30 minutes. Last: 2:25 PM Pacific

 
   |  Out, damned space

posted at 02:48 AM | | [8] |

The other day I was doing something where I wanted to display some data as a comma-delimited list, like so:

a, b, c, d

This seemed like a job for the Repeater control. So I created the markup for the control and included a separator template, like this:

<asp:Repeater runat="server" ID="Repeater1" DataSourceID="LinqDataSource3"  >
<ItemTemplate>
<asp:Label runat="server" ID="labelCategory" Text='<%# Eval("Category") %>'>
</asp:Label>
</ItemTemplate>
<SeparatorTemplate>, </SeparatorTemplate>
</asp:Repeater>

The result was this:

a , b , c, d

One thing was right (no trailing comma). But there was that silly space between the data item and the delimiter.

I spent as much as a minute looking to see if there was an easy fix for this. Failing in this effort, I invented a solution that seems ... well ... kinda complex, considering. So this is where the Internet tells me that a) I'm using the wrong control and/or b) alls you have to do is set a property, duh.

In the meantime, tho, what I ended up doing was this: I handled the Repeater control's PreRender event, which is well after data binding and about the last time you can go in and mess about with the markup that the control will render. In that event, I walked the contents of the Repeater control to, in effect, trim spaces.

But it wasn't quite as simple as I thought. Instead of being able to grub around directly -- in a simple string that represented the markup being rendered, say -- I was obliged to walk the control tree that was inside the Items collection exposed by the control. And even then, I did not get access to the actual delimiters, only to the data items. (This would be another good place for someone to tell me how I managed to overlook the easy way.)

The, dunno, peculiarity of the way that the Repeater renders is that the controls being rendered for each data item go something like this:

[LiteralControl][Bound control][LiteralControl]

IOW, the Repeater control brackets the data-bound control (in the example above, the Label control) with a LiteralControl instance on each side that pads the data with a space. Or that's what I was seeing, anyway. I'm not so clear on why it does this, but poking around inside the Items collection for each data element seemed to show this. (Is this the design? How come?)

What I ended up doing, then, was walking through the Items collection to get each item's control collection. Then I walked the control collection, and whenver I found a LiteralControl, I set its text to an empty string. If I found a Label control or Hyperlink control, I did a trim on it. Here's the code:

Public Sub RepeaterTrimItems(ByVal source As Object, ByVal e As EventArgs)
Dim r As Repeater = CType(source, Repeater)
For itemCtr As Integer = 0 To r.Items.Count - 1
For controlCtr As Integer = 0 To r.Items(itemCtr).Controls.Count - 1
Dim controlObj As Object = r.Items(itemCtr).Controls(controlCtr)
Dim controlType As Type = controlObj.GetType()
Select Case controlType.ToString().ToUpper()
Case "SYSTEM.WEB.UI.LITERALCONTROL"
Dim lit As LiteralControl = CType(controlObj, LiteralControl)
lit.Text = lit.Text.Trim()
If lit.Text = " " Then
lit.Text = ""
End If
Case "SYSTEM.WEB.UI.WEBCONTROLS.LABEL"
Dim lab As Label = CType(controlObj, Label)
lab.Text = lab.Text.Trim()
Case "SYSTEM.WEB.UI.WEBCONTROLS.HYPERLINK"
Dim hlink As HyperLink = CType(controlObj, HyperLink)
hlink.Text = hlink.Text.Trim()
End Select
Next
Next
End Sub

There are a number of ways in which this is messy. It's hard-coded to work for a Repeater control, for one. Then in order to be able to set the Text property of any given control it encounters, it has to cast the objects in the Controls collection to an appropriate type. To do that, I have to get the type (GetType), and then I have a messy Select block that is (again) hard-coded to look for certain control types only. (This feels like something that could be done a lot more elegantly.) On the plus side, it works. To get it hooked up, all I do is this, on any Repeater control:

<asp:Repeater runat="server" ID="Repeater1" 
DataSourceID="LinqDataSource3"
OnPreRender="RepeaterTrimItems" >
<ItemTemplate>
<asp:Label runat="server" ID="labelCategory"
Text='<%# Eval("Category") %>'>
</asp:Label>
</ItemTemplate>
<SeparatorTemplate>, </SeparatorTemplate>
</asp:Repeater>

Having done all this (once you get started, you sort of get caught up in the challenge), I took a step back and thought "But surely this isn't necessary for the slick new ListView control." But as far as I can tell, it's the same. I have a function that does virtually the same thing for the ListView control, the only difference being that I use an ItemSeparatorTemplate instead:

<asp:ListView ID="ListView2" runat="server" 
DataSourceID="LinqDataSource3"
OnPreRender="TrimListView">
<ItemTemplate>
<asp:Label runat="server"
ID="labelCategory"
Text='<%# Eval("Category") %>'>
</asp:Label>
</ItemTemplate>
<ItemSeparatorTemplate>,</ItemSeparatorTemplate>

<LayoutTemplate>
<div ID="itemPlaceholderContainer" runat="server" style="">
<span runat="server" id="itemPlaceholder" />
</div>
</LayoutTemplate>
</asp:ListView>

Again, I'd be delighted to hear about how this is supposed to be done for real. In the meantime, tho, I have my comma-separated list.

[categories]