The central component of the College.NET application is the AnchorCloud control. It is a databound custom web control that renders a cloud/grid of hyperlinks with appropriate tooltips, comments, and formatting. All source code markup was produced by the excellent CopySourceAsHtml Visual Studio 2005 Add-In.
1 Imports System
2 Imports System.Web
3 Imports System.Web.UI.WebControls
4
5 '// The AnchorCloud control is a Databound control.
6 '// It is bound to the results of the qCollegeReflections query.
7 '// It renders the underlying data as a grid/cloud of hyperlinks.
8 '// The control also tries to minimize the size of the output by tracking style changes.
9
10 <ToolboxData("<{0}:AnchorCloud runat=server></{0}:AnchorCloud>")> _
11 Public Class AnchorCloud
12 Inherits DataBoundControl
13
14 Public Enum RenderStyles
15 Cloud = 0
16 Grid = 1
17 End Enum
18
19 '// Public property placeholders
20 Private _renderStyle As RenderStyles
21
22 '// Support Databound control properties
23 Private _data As IEnumerable
24 Private _dataTextField As String = String.Empty
25 Private _sortExpression As String = String.Empty
26
27 '// Non-breaking hyphen depends on browser type
28 '// Calculate it once, then store it here
29 Private _nonBreakingHyphen As String
30
31
32 '// Specify how the the control is rendered.
33 '// Valid choices are to render as a grid or as a cloud (of hyperlinks)
34 Public Property RenderStyle() As RenderStyles
35 Get
36 Return _renderStyle
37 End Get
38 Set(ByVal value As RenderStyles)
39 _renderStyle = value
40 End Set
41 End Property
42
43
44 '// Specify how to sort the output from the underlying data object
45 '// Value is passed through to underlying databound control methods
46 Public Property SortExpression() As String
47 Get
48 Return _sortExpression
49 End Get
50 Set(ByVal value As String)
51 _sortExpression = value
52 End Set
53 End Property
54
55
56 '// Override WebControl.RenderContents
57 '// Call the appropriate routine, depending on the render mode.
58 Protected Overrides Sub RenderContents(ByVal writer As HtmlTextWriter)
59 PrepareNonBreakingHyphen(writer)
60
61 If DesignMode Then
62 RenderDesigner(writer)
63 Return
64 End If
65
66 Select Case RenderStyle
67 Case RenderStyles.Grid
68 RenderGrid(writer)
69 Case Else
70 RenderCloud(writer)
71 End Select
72 End Sub
73
74
75 '// Override of DataBoundControl.PerformSelect method
76 '// Bind the control to the specified datasource
77 Protected Overrides Sub PerformSelect()
78 '// This is the Microsoft recommended approach to implementing a databound control.
79 '// It would be better if it were encapsulated in a class/interface.
80 '// However, since this is a one-off project, just use it directly.
81
82 Dim dataSourceSelectArguments As DataSourceSelectArguments
83 Dim dataSourceView As DataSourceView
84
85 If Not IsBoundUsingDataSourceID Then
86 Me.OnDataBinding(EventArgs.Empty)
87 End If
88
89 dataSourceView = Me.GetData()
90
91 dataSourceSelectArguments = New DataSourceSelectArguments
92 If dataSourceView.CanSort = True Then
93 If Me.SortExpression.Length > 0 Then
94 dataSourceSelectArguments.SortExpression = Me.SortExpression
95 End If
96 End If
97
98 dataSourceView.Select(dataSourceSelectArguments, AddressOf PerformDataBinding)
99
100 RequiresDataBinding = False
101 MarkAsDataBound()
102 OnDataBound(EventArgs.Empty)
103 End Sub
104
105 '// Override DataBoundControl.PerformDataBinding
106 '// This is called when the asynchronous DataSourceView.Select() method completes
107 Protected Overrides Sub PerformDataBinding(ByVal data As System.Collections.IEnumerable)
108 MyBase.PerformDataBinding(data)
109
110 '// Store a local copy of the data for later use
111 _data = data
112 End Sub
113
114
115 '// Render design mode version of control (silver block with control ID)
116 Private Sub RenderDesigner(ByVal writer As HtmlTextWriter)
117 writer.Write("<table cellpadding=4 cellspacing=0 bordercolor=black border=1>")
118 writer.Write("<tr>")
119 writer.Write("<td bgcolor=silver>")
120 writer.Write("<b>AnchorCloud</b> - " & Me.ID)
121 If Me.DataSourceID.Length > 0 Then
122 writer.Write(" (DataBound - " & HttpUtility.HtmlEncode(Me.DataSourceID) & ")")
123 Else
124 writer.Write(" (Unbound)")
125 End If
126 writer.Write("</td></tr></table></span>")
127 Exit Sub
128 End Sub
129
130
131 '// Render contents of control as a Cloud (series of Html Hyperlinks)
132 Private Sub RenderCloud(ByVal writer As HtmlTextWriter)
133 Dim dataElement As Object
134
135 Dim reflectionID As Integer
136 Dim rating As Integer
137 Dim shortName As String
138 Dim hyperlink As String
139 Dim tooltip As String
140 Dim course As String
141
142 Dim fontSize As Integer
143 Dim fontBold As Boolean
144 Dim lastFontSize As Integer
145 Dim fontNeedsEndTag As Boolean
146
147 Dim htmlClass As String
148
149 writer.BeginRender()
150
151 writer.AddAttribute("class", "CloudView", False)
152 writer.RenderBeginTag("span")
153
154 If Not _data.GetEnumerator.MoveNext() Then
155 writer.Write("No records match your criteria")
156 End If
157
158 fontSize = 3
159 lastFontSize = 3
160 fontBold = False
161 writer.AddAttribute("size", CStr(fontSize), False)
162 writer.RenderBeginTag("font")
163 fontNeedsEndTag = False
164
165 For Each dataElement In _data
166 reflectionID = CInt(DataBinder.GetPropertyValue(dataElement, "ReflectionID"))
167 rating = CInt(DataBinder.GetPropertyValue(dataElement, "Rating"))
168 shortName = CStrSafe(DataBinder.GetPropertyValue(dataElement, "ShortName"))
169 hyperlink = CStrSafe(DataBinder.GetPropertyValue(dataElement, "Hyperlink"))
170 course = CStrSafe(DataBinder.GetPropertyValue(dataElement, "CourseNumber")) & " - " & CStrSafe(DataBinder.GetPropertyValue(dataElement, "CourseName"))
171 tooltip = CStrSafe(DataBinder.GetPropertyValue(dataElement, "Tooltip"))
172 If tooltip.Length > 0 Then
173 tooltip = tooltip & " (" & course & ")"
174 Else
175 tooltip = course
176 End If
177
178 htmlClass = "R" & rating
179 fontSize = 3
180 fontBold = False
181 Select Case rating
182 Case 1
183 fontSize = 3
184 fontBold = True
185 Case 2
186 fontSize = 4
187 fontBold = False
188 Case 3
189 fontSize = 4
190 fontBold = True
191 Case 4
192 fontSize = 5
193 fontBold = True
194 Case 5
195 fontSize = 6
196 fontBold = True
197 End Select
198
199 If fontSize > 6 Then
200 fontSize = 6
201 End If
202
203 If fontSize <> lastFontSize Then
204 If fontNeedsEndTag Then
205 writer.RenderEndTag() '// font
206 fontNeedsEndTag = False
207 lastFontSize = 3
208 End If
209 End If
210
211 writer.Write(" ")
212 writer.Write(vbNewLine)
213
214 If fontSize <> lastFontSize Then
215 writer.AddAttribute("size", CStr(fontSize), False)
216 writer.RenderBeginTag("font")
217 fontNeedsEndTag = True
218 lastFontSize = fontSize
219 End If
220
221 If fontBold Then
222 writer.RenderBeginTag("b")
223 End If
224
225 If hyperlink.Length > 0 Then
226 writer.AddAttribute("href", hyperlink, True)
227 If tooltip.Length > 0 Then
228 writer.AddAttribute("title", tooltip, True)
229 End If
230 writer.AddAttribute("class", htmlClass, False)
231
232 writer.RenderBeginTag("a")
233 writer.Write(HtmlEncodedNonBreakingText(shortName))
234 writer.RenderEndTag()
235 Else
236 If tooltip.Length > 0 Then
237 writer.AddAttribute("class", htmlClass, False)
238 writer.AddAttribute("title", tooltip, True)
239 writer.RenderBeginTag("span")
240 writer.RenderBeginTag("u")
241 End If
242
243 writer.Write(HtmlEncodedNonBreakingText(shortName))
244
245 If tooltip.Length > 0 Then
246 writer.RenderEndTag() '// u
247 writer.RenderEndTag() '// span
248 End If
249 End If
250
251 If fontBold Then
252 writer.RenderEndTag() '// b
253 End If
254
255 writer.Flush()
256 Next
257
258 If fontNeedsEndTag Then
259 writer.RenderEndTag() '// font
260 End If
261
262 writer.RenderEndTag() '// font
263
264 writer.RenderEndTag() '// span
265
266 writer.EndRender()
267 End Sub
268
269
270 '// Render contents of control as a Grid (Html Table)
271 Private Sub RenderGrid(ByVal writer As HtmlTextWriter)
272 Dim dataElement As Object
273
274 Dim rating As Integer
275 Dim shortName As String
276 Dim hyperlink As String
277 Dim tooltip As String
278 Dim courseNumber As String
279 Dim courseName As String
280 Dim course As String
281 Dim semester As String
282
283 Dim fontSize As Integer
284 Dim fontBold As Boolean
285
286 Dim recordCount As Integer
287
288 Dim htmlClass As String
289
290 writer.BeginRender()
291
292 writer.AddAttribute("class", "GridView", False)
293 writer.RenderBeginTag("span")
294
295 recordCount = 0
296 If Not _data.GetEnumerator.MoveNext() Then
297 writer.Write("No records match your criteria")
298 Return
299 End If
300
301 writer.AddAttribute("border", "1", False)
302 writer.AddAttribute("cellpadding", "4", False)
303 writer.AddAttribute("cellspacing", "0", False)
304 writer.Write(vbNewLine)
305 writer.RenderBeginTag("table")
306
307 writer.AddAttribute("bgcolor", "silver", False)
308 writer.Write(vbNewLine)
309 writer.RenderBeginTag("thead")
310
311 writer.Write(vbNewLine)
312 writer.RenderBeginTag("th")
313 writer.Write("Semester")
314 writer.RenderEndTag()
315
316 writer.Write(vbNewLine)
317 writer.RenderBeginTag("th")
318 writer.Write("Course Number")
319 writer.RenderEndTag()
320
321 writer.Write(vbNewLine)
322 writer.RenderBeginTag("th")
323 writer.Write("Course Name")
324 writer.RenderEndTag()
325
326 writer.Write(vbNewLine)
327 writer.RenderBeginTag("th")
328 writer.Write("Subject")
329 writer.RenderEndTag()
330
331 writer.Write(vbNewLine)
332 writer.RenderBeginTag("th")
333 writer.Write("Description")
334 writer.RenderEndTag()
335
336 writer.RenderEndTag() '// thead
337
338 For Each dataElement In _data
339
340 recordCount = recordCount + 1
341
342 writer.Write(vbNewLine)
343 writer.RenderBeginTag("tr")
344
345 rating = CInt(DataBinder.GetPropertyValue(dataElement, "Rating"))
346 shortName = CStrSafe(DataBinder.GetPropertyValue(dataElement, "ShortName"))
347 hyperlink = CStrSafe(DataBinder.GetPropertyValue(dataElement, "Hyperlink"))
348 courseNumber = CStrSafe(DataBinder.GetPropertyValue(dataElement, "CourseNumber"))
349 courseName = CStrSafe(DataBinder.GetPropertyValue(dataElement, "CourseName"))
350 course = courseNumber & " - " & courseName
351 semester = CStrSafe(DataBinder.GetPropertyValue(dataElement, "Semester")) & " " & CStrSafe(DataBinder.GetPropertyValue(dataElement, "Year"))
352 tooltip = CStrSafe(DataBinder.GetPropertyValue(dataElement, "Tooltip"))
353
354 htmlClass = "R" & rating
355 fontSize = 3
356 fontBold = False
357 Select Case rating
358 Case 1
359 fontSize = 3
360 fontBold = True
361 Case 2
362 fontSize = 4
363 fontBold = False
364 Case 3
365 fontSize = 4
366 fontBold = True
367 Case 4
368 fontSize = 5
369 fontBold = False
370 Case 5
371 fontSize = 5
372 fontBold = True
373 End Select
374
375 If fontSize > 6 Then
376 fontSize = 6
377 End If
378
379 writer.AddAttribute("valign", "top", False)
380 writer.Write(vbNewLine)
381 writer.RenderBeginTag("td")
382 writer.Write(HtmlEncodedNonBreakingText(semester))
383 writer.RenderEndTag()
384
385 writer.AddAttribute("valign", "top", False)
386 writer.Write(vbNewLine)
387 writer.RenderBeginTag("td")
388 writer.Write(HtmlEncodedNonBreakingText(courseNumber))
389 writer.RenderEndTag()
390
391 writer.AddAttribute("valign", "top", False)
392 writer.Write(vbNewLine)
393 writer.RenderBeginTag("td")
394 writer.Write(HttpUtility.HtmlEncode(courseName))
395 writer.RenderEndTag()
396
397 writer.AddAttribute("valign", "top", False)
398 writer.Write(vbNewLine)
399 writer.RenderBeginTag("td")
400
401 If fontSize <> 3 Then
402 writer.AddAttribute("size", CStr(fontSize), False)
403 writer.RenderBeginTag("font")
404 End If
405
406 If fontBold Then
407 writer.RenderBeginTag("b")
408 End If
409
410 If hyperlink.Length > 0 Then
411 writer.AddAttribute("class", htmlClass, False)
412 writer.AddAttribute("href", hyperlink, True)
413 writer.RenderBeginTag("a")
414 writer.Write(HttpUtility.HtmlEncode(shortName))
415 writer.RenderEndTag()
416 Else
417 writer.AddAttribute("class", htmlClass, False)
418 writer.RenderBeginTag("span")
419 writer.Write(HttpUtility.HtmlEncode(shortName))
420 writer.RenderEndTag()
421 End If
422
423 If fontBold Then
424 writer.RenderEndTag() '// b
425 End If
426
427 If fontSize <> 3 Then
428 writer.RenderEndTag() '// font
429 End If
430
431 writer.RenderEndTag() '// td
432
433 writer.AddAttribute("valign", "top", False)
434 writer.Write(vbNewLine)
435 writer.RenderBeginTag("td")
436 If tooltip.Length > 0 Then
437 writer.Write(HttpUtility.HtmlEncode(tooltip))
438 Else
439 writer.Write(" ")
440 End If
441
442 writer.RenderEndTag() '// td
443
444 writer.RenderEndTag() '// tr
445
446 writer.Flush()
447 Next
448
449 writer.RenderEndTag() '// table
450
451 writer.RenderEndTag() '// span
452
453 writer.EndRender()
454 End Sub
455
456
457 '// Prevent output from being wrapped by the browser (keep words together)
458 Private Function HtmlEncodedNonBreakingText(ByVal text As String) As String
459 If text.Length = 0 Then
460 Return String.Empty
461 End If
462
463 Dim result As String
464
465 result = text
466
467 result = HttpUtility.HtmlEncode(result)
468 result = Replace(result, " ", " ")
469 result = Replace(result, "-", _nonBreakingHyphen)
470
471 Return result
472 End Function
473
474 '// String conversion might need to change depending on type.
475 '// Therefore, encapsulate the function here, just in case it needs to change later
476 Private Function CStrSafe(ByVal item As Object) As String
477 Return item.ToString()
478 End Function
479
480
481 '// Not all browsers deal with non-breaking hypens equally.
482 '// This function will determine the best character to use.
483 Sub PrepareNonBreakingHyphen(ByVal writer As HtmlTextWriter)
484 Dim browser As String
485
486 _nonBreakingHyphen = "‑" '// equivalent "‑"
487
488 '// Check if Browser object is available
489 If Me.DesignMode OrElse _
490 Me.Page Is Nothing _
491 OrElse Me.Page.Request Is Nothing _
492 OrElse Me.Page.Request.Browser Is Nothing Then
493
494 '// Browser object is not available, exit now.
495 Return
496 End If
497
498 '// Determine which browser is being used
499 browser = Me.Page.Request.Browser.Browser
500
501 '// Choose appropriate non-breaking hyphen character, depending on browser type
502 Select Case LCase(browser)
503 Case "ie", "firefox"
504 '// These browsers can render non-breaking hyphens.
505 '// Therefore, do nothing.
506 Case Else
507 '// Any other browsers might have problems with rendering non-breaking hyphens.
508 '// Therefore, use the tilde character instead.
509 _nonBreakingHyphen = "~"
510 End Select
511 End Sub
512 End Class