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