Aufgrund des aktuell recht miesen Ladeverhaltens der auf zerbit.de bislang verwendeten live-Searchbox für die Suche in diesem Web, habe ich mich entschlossen BOSS (Build your own Search Engine) von Yahoo einmal auszuprobieren. Schön an dieser Lösung ist, daß man die Suchergebnisse über eine API im XML-Format abrufen und sie dann nahtlos in das Layout der eigenen Seite integrieren kann.

Notwendig war in diesem Zusammenhang natürlich auch ein Seiten-Navigator, wie man ihn auf Suchergebnisseiten gewohnt ist. Je nachdem wieviele Gesamttreffer eine Suche ergibt und wieviele Treffer auf einer Seite dargestellt werden, sollten unterschiedlich viele Seiten-Links (dargestellt als kleine Buttons) im Navigator enthalten sein, jedoch maximal 15 um einzeilig zu bleiben. Bei der Anzeige einer Seite größer als 15, sollte der Navigator "nach rechts rollen", d.h. der erste darzustellende Seiten-Link sollte sich um eins verschieben, bis die maximale Anzahl an Seiten erreicht ist.
Möglichkeiten
Eine mögliche Lösung für die Implementierung des Navigators wären statische Hyperlink-Controls gewesen, die in Abhängigkeit der anzuzeigenden Trefferseite ihren Text und ihren Link ändern, aber damit wäre die maximale Anzahl an Seitenlinks per Code auf 15 zementiert und wenig Raum für eine schnelle Anpassung, wenn es dann doch einmal mehr sein sollten. Die größte Flexibilität wäre erreicht, wenn man in der web-config definieren könnte, wie "breit" der Navigator sein soll und der Code dies dynamisch in HTML umsetzt.
Für dieses Problem bietet ASP.NET über die Methode Controls.Add die Möglichkeit zur Laufzeit Steuerelemente in einen beliebigen Container einzufügen, wie unter anderem Uwe Grone in seinem MSDN Solve Artikel beschreibt.
Dim lnk As HyperLink = New HyperLink()
lnk.Text = "..."
lnk.NavigateUrl = "..."
Me.Panel1.Controls.Add(lnk)
Dieser Ansatz hat allerdings den Haken, dass das Hyperlink-Control keinen Konstruktor bietet, mit dem man die notwendigen Attribute direkt übergeben kann. Bei einer nicht bekannten Anzahl an zu erzeugenden Controls kommt da recht viel Code zustande, vor allem, wenn man zwei Navigatoren braucht: einen oben auf der Seite und den anderen unten.
Das weit aus Beste wäre, schlicht puren HTML-Code als Text in einer Schleife zu erzeugen und diesen dann in den Container zu "injizieren". Man hätte die größtmögliche Kontrolle über die Ausgabe und könnte den HTML-Code mehrmals verwenden.
ParseControl
Auch dafür bietet ASP.NET eine Lösung. Eine recht unscheinbare, aber wenn man genauer darüber nachdenkt, wohl eine der mächtigsten Methoden bei der Erstellung von HTML-Code: ParseControl. Sie ist in der Lage zur Laufzeit aus übergebenem Markup Steuerelemente zu erstellen. Dies funktioniert nicht nur mit simplen HTML-Controls, sondern ebenfalls mit WebServer- und anderen Controls. Abgeleitet von TemplateControl steht ParseControl in allen UserControls und vor allem im Page-Objekt zur Verfügung und stellt eine Art Control-Evaluator dar.
Der Seitennavigator
Mit dieser Methode ist die Erstellung des dynamischen Seitennavigators ein Kinderspiel. Zur Erstellung des Navigator-Control-Strings, den ParseControl auswerten soll, sind zuvor allerdings ein paar Berechnungen notwendig.
Zunächst wird die Gesamtzahl der Treffer benötigt, die Yahoo BOSS im XML mitliefert: intTotalHits. Die maximale Anzahl an Suchtreffern pro Seite (intMaxHitsPerPage) und die maximale Anzahl der Seiten-Links im Navigator (intMaxPages) wird aus der web.config ausgelesen. Um die erste und die letzte anzuzeigende Seite im Navigator zu bestimmen, müssen die Rotationspunkte errechnet werden, sprich ab welcher Seite verschiebt sich die erste Seite um eins (intRotationLeft) und wann hört die Verschiebung auf (intRotationRight).
Mit Übergabe der anzuzeigenden Trefferseite (intPage) sieht die Berechnung dann wie folgt aus:
Dim intTotalHits As Integer = (... aus Yahoo-BOSS-XML)
Dim intMaxHitsPerPage As Integer = (...aus web-config)
Dim intMaxPages As Integer = (...aus web-config)
Dim intPage As Integer = (... aus Request.QueryString)
'Gesamtseitenzahl ermitteln
Dim intTotalPages As Integer = intTotalHits \ intMaxHitsPerPage
If intTotalHits Mod intMaxHitsPerPage > 0 Then intTotalPages += 1
'Erste und letzte Seite im Navigator errechnen
Dim intFirstPage As Integer = 1
Dim intLastPage As Integer = intMaxPages
If intMaxPages > intTotalPages Then intLastPage = intTotalPages
Dim intRotationLeft As Integer = intMaxPages \ 2
If intMaxPages Mod 2 > 0 Then intRotationLeft += 1
Dim intRotationRight As Integer = intTotalPages - intRotationLeft - 1
If intTotalPages > intMaxPages And intPage > intRotationLeft Then
If intPage <= intRotationRight Then
intFirstPage = intPage - intRotationLeft + 1
Else
intFirstPage = intTotalPages - intMaxPages + 1
End If
If intPage <= intRotationRight Then
intLastPage = intFirstPage + intMaxPages - 1
Else
intLastPage = intTotalPages
End If
End If
Der Rest ist ein simples Zusammenstellen eines Strings aus A-Elementen und einem STRONG-Element mittels des StringBuilders...
Dim stbNav As New StringBuilder
Dim strPage As String = ResolveClientUrl("~/search.aspx?s=" & strSearchString)
'eine Seite zurück
If intPage > 1 Then
stbNav.Append("<a href=""" & strPage & "&page=" & intPage - 1 & """>zurück</a>")
End If
'Seiten bis aktuelle
For i As Integer = intFirstPage To intPage - 1
stbNav.Append("<a href=""" & strPage & "&page=" & i & """>" & i & "</a>")
Next
'aktuelle Seite
stbNav.Append("<strong>" & intPage & "</strong>")
'Seiten ab aktueller
For i As Integer = intPage + 1 To intMaxPages
stbNav.Append("<a href=""" & strPage & "&page=" & i & """>" & i & "</a>")
Next
'eine Seite vor
If intPage < intLastPage Then
stbNav.Append("<a href=""" & strPage & "&page=" & intPage + 1 & """>weiter</a>")
End If
... und die Ausgabe des Strings über ParseControl in je einen Panel-Container am Anfang und am Ende der Seite.
'Navigator in Container einfügen
Me.panNavTop.Controls.Add(ParseControl(stbNav.ToString))
Me.panNavBottom.Controls.Add(ParseControl(stbNav.ToString))
Fazit
Anhand dieses Beispiels kann man sich vorstellen, wie schnell und einfach man über ParseControl Seiten dynamisch generieren kann. Machbar wären so zum Beispiel auch komplett aus Datenbanken heraus erzeugte Web-Seiten.
