Bei fast allen Webanwendungen zur Datenpräsentation ist es eines der Hauptziele, die Art und Weise der Datenanzeige zu organisieren und verwirrende Gefühle für Benutzer zu vermeiden. Die Anzeige von 20 Datensätzen pro Seite ist sicherlich akzeptabel, aber die Anzeige von 10.000 Datensätzen pro Seite kann für Benutzer leicht zu Unannehmlichkeiten führen. Die gebräuchlichste Methode zur Lösung solcher Probleme ist die Aufteilung der Daten zur Anzeige auf mehrere Seiten, also das Paginieren der Daten.
1. Übersicht
ASP.NET selbst bietet nur ein Steuerelement, das Daten-Paging unterstützt, nämlich das DataGrid-Paging-Steuerelement. Für die Verwendung in Intranet-Umgebungen scheinen die vom DataGrid-Paging-Steuerelement bereitgestellten Funktionen jedoch nicht geeignet zu sein ausreichen, um eine flexible Webanwendung zu erstellen. Einer der Gründe dafür ist, dass das DataGrid-Steuerelement Einschränkungen hinsichtlich der Platzierung von Paging-Steuerelementen durch Webdesigner und des Erscheinungsbilds von Paging-Steuerelementen auferlegt. Beispielsweise lässt das DataGrid-Steuerelement keine vertikale Platzierung von Paging-Steuerelementen zu. Ein weiteres Steuerelement, das die Paging-Technologie nutzen kann, ist Repeater. Webentwickler können das Repeater-Steuerelement verwenden, um die Anzeigemethode von Daten schnell zu konfigurieren, aber die Paging-Funktion erfordert, dass Entwickler sie selbst implementieren. Datenquellen ändern sich ständig und die Methoden zur Datenpräsentation sind offensichtlich Zeitverschwendung, wenn man eine universelle Paging-Steuerung erstellen möchte, die nicht auf bestimmte Präsentationssteuerungen beschränkt ist.
Eine hervorragende universelle Datensteuerung bietet nicht nur regelmäßige Paging-Funktionen, sondern kann auch:
⑴ Stellen Sie die Navigationsschaltflächen „Startseite“, „Vorherige Seite“, „Nächste Seite“ und „Letzte Seite“ bereit.
⑵ Passen Sie seinen Status entsprechend der Datenanzeigesituation an, d. h. er verfügt über Datensensibilität. Wenn das Paging-Steuerelement auf die Anzeige von 10 Datensätzen pro Seite eingestellt ist, tatsächlich aber nur 9 Datensätze vorhanden sind, sollte das Paging-Steuerelement nicht angezeigt werden, wenn die Daten zur Anzeige in mehrere Seiten unterteilt sind: „Home“ und „Top“. die erste Seite Die Schaltfläche „Seite“ sollte nicht angezeigt werden, ebenso sollten die Schaltflächen „Nächste Seite“ und „Letzte Seite“ der letzten Seite nicht angezeigt werden.
⑶ Kann sich nicht auf bestimmte Steuerelemente für die Datenanzeige verlassen.
⑷ Fähigkeit zur Anpassung an verschiedene bestehende und zukünftige Datenquellen.
⑸ Der Anzeigemodus sollte einfach zu konfigurieren und leicht in verschiedene Anwendungen zu integrieren sein.
⑹ Wenn das Paging bereit ist, erinnern Sie andere Steuerelemente daran.
⑺ Auch unerfahrene Webdesigner sollten damit problemlos umgehen können.
⑻ Geben Sie Attributdaten zu Paging-Informationen an.
Derzeit gibt es einige kommerzielle Steuerungen auf dem Markt, die die oben genannten Funktionen bieten, diese sind jedoch alle teuer. Für viele Entwickler ist es die ideale Wahl, selbst ein universelles Paging-Steuerelement zu erstellen.
Abbildung 1 zeigt die laufende Schnittstelle des universellen Paging-Steuerelements in diesem Artikel, wobei das für die Anzeige verwendete Steuerelement ein Repeater-Steuerelement ist. Die Seitensteuerung besteht aus zwei Arten von Komponenten: vier Navigationsschaltflächen und einer Reihe von Seitenzahl-Links.
Benutzer können die Anzeigesteuerelemente einfach ändern und das Erscheinungsbild des Paging-Steuerelements selbst ändern. Beispielsweise wird das Anzeigesteuerelement, das mit dem Paging-Steuerelement zusammenarbeitet, durch ein DataGrid-Steuerelement ersetzt und der Seitennummernlink sowie vier Navigationsschaltflächen werden in zwei Zeilen angezeigt.
ASP.NET unterstützt drei Möglichkeiten zum Erstellen benutzerdefinierter Websteuerelemente: Benutzersteuerelemente, zusammengesetzte Steuerelemente und benutzerdefinierte Steuerelemente. Der Name des dritten Steuerelementtyps, des benutzerdefinierten Steuerelements, ist leicht irreführend. Tatsächlich sollten alle drei Steuerelemente als benutzerdefinierte Steuerelemente betrachtet werden. Der Unterschied zwischen zusammengesetzten Steuerelementen und den sogenannten benutzerdefinierten Steuerelementen von Microsoft besteht darin, dass erstere die Methode CreateChildControls() erfordern. Die Methode CreateChildControls() ermöglicht es dem Steuerelement, sich basierend auf bestimmten Ereignissen neu zu zeichnen. Für den universellen Pager dieses Artikels verwenden wir ein zusammengesetztes Steuerelement.
Das folgende UML-Sequenzdiagramm beschreibt den allgemeinen Mechanismus der universellen Paging-Steuerung.
Obwohl unser Ziel darin besteht, die universelle Paging-Steuerung unabhängig von der Steuerung zu machen, die die Daten darstellt, ist es offensichtlich, dass es für die Paging-Steuerung eine Möglichkeit geben muss, auf die Daten zuzugreifen. Jedes Steuerelement, das von der Control-Klasse erbt, stellt ein DataBinding-Ereignis bereit. Wir registrieren den Pager selbst als Listener für das DataBinding-Ereignis, damit der Pager mehr über die Daten erfahren und die Daten ändern kann. Da alle Steuerelemente, die von der Control-Klasse erben, über dieses DataBinding-Ereignis verfügen, erreicht das Pager-Steuerelement das Ziel, nicht auf ein bestimmtes Datenpräsentationssteuerelement angewiesen zu sein – mit anderen Worten: Das Pager-Steuerelement kann an alle Steuerelemente gebunden werden, die von der Control-Klasse abgeleitet sind. Das heißt, es kann an fast alle Websteuerelemente gebunden werden.
2. Kernfunktionen
Wenn das Präsentationssteuerelement das DataBinding-Ereignis auslöst, kann das Paging-Steuerelement die DataSource-Eigenschaft abrufen. Leider stellt Microsoft keine Schnittstellen bereit, die alle Datenbindungsklassen implementieren, wie z. B. IdataSourceProvider, und nicht alle Steuerelemente, die von der Control- oder WebControl-Klasse erben, verfügen über eine DataSource-Eigenschaft, sodass ein Upcasting in die Control-Klasse keinen Sinn macht, sondern die einzig mögliche Weg Die Methode besteht darin, die DataSoruce-Eigenschaft direkt über die Reflection-API zu bedienen. Bevor wir die Event-Handler-Methoden besprechen, sollte beachtet werden, dass Sie zum Registrieren eines Event-Handlers zunächst einen Verweis auf das Präsentationssteuerelement erhalten müssen. Das Paging-Steuerelement stellt eine einfache Zeichenfolgeneigenschaft BindToControl bereit:
öffentliche Zeichenfolge BindToControl
{
erhalten
{
if (_bindcontrol == null)
throw new NullReferenceException("Bevor Sie das Paging-Steuerelement verwenden, binden Sie es bitte an ein Steuerelement, indem Sie die BindToControl-Eigenschaft festlegen.");
return _bindcontrol;}
set{_bindcontrol=value;}
}
Diese Methode ist sehr wichtig, daher ist es am besten, eine klarere Nachricht auszulösen, anstatt die Standard-NullReferenceException auszulösen. In der OnInit-Methode des Paging-Controls lösen wir den Verweis auf das Präsentations-Control auf. In diesem Beispiel sollte der OnInit-Ereignishandler (anstelle des Konstruktors) verwendet werden, um sicherzustellen, dass auf der JIT-kompilierten ASPX-Seite BindToControl festgelegt ist.
protected override void OnInit(EventArgs e)
{
_boundcontrol = Parent.FindControl(BindToControl);
BoundControl.DataBinding += new EventHandler(BoundControl_DataBound);
base.OnInit(e);
...
}
Der Vorgang des Durchsuchens des Präsentationssteuerelements wird durch das Durchsuchen des übergeordneten Steuerelements des Paging-Steuerelements abgeschlossen. Hier ist das übergeordnete Element die Seite selbst. Es ist gefährlich, Parent auf diese Weise zu verwenden. Wenn das Paging-Steuerelement beispielsweise in ein anderes Steuerelement, beispielsweise ein Tabellensteuerelement, eingebettet ist, handelt es sich bei der Parent-Referenz tatsächlich um einen Verweis auf das Tabellensteuerelement. Da die FindControl-Methode nur die aktuelle Steuerelementsammlung durchsucht, ist eine Suche nur möglich, wenn sich das Präsentationssteuerelement in der Sammlung befindet. Ein sichererer Ansatz besteht darin, die Sammlung von Steuerelementen rekursiv zu durchsuchen, bis das Zielsteuerelement gefunden wird.
Nachdem wir das BoundControl gefunden haben, registrieren wir das Paging-Steuerelement als Listener für das DataBinding-Ereignis. Da die Paging-Steuerung auf einer Datenquelle basiert, ist es wichtig, dass dieser Event-Handler der letzte in der Aufrufkette ist. Solange das Präsentationssteuerelement jedoch den DataBinding-Ereignishandler im OnInit-Ereignishandler registriert (das Standardverhalten), gibt es kein Problem, wenn das Paging-Steuerelement die Datenquelle betreibt.
Der DataBound-Ereignishandler ist dafür verantwortlich, die DataSource-Eigenschaft des Präsentationssteuerelements abzurufen.
private void BoundControl_DataBound(object sender,System.EventArgs e)
{
if (HasParentControlCalledDataBinding) return;
Typ type = sender.GetType();
_datasource = type.GetProperty("DataSource");
if (_datasource == null)
throw new NotSupportedException("Das Paging-Steuerelement erfordert, dass das Präsentationssteuerelement eine DataSource enthalten muss.");
Objektdaten = _datasource.GetGetMethod().Invoke(sender,null);
_builder = Adapters[data.GetType()];
if (_builder == null)
throw new NullReferenceException("Der entsprechende Adapter ist nicht installiert, um den folgenden Datenquellentyp zu verarbeiten: "+data.GetType());
_builder.Source = data;
ApplyDataSensitivityRules();
BindParent();
RaiseEvent(DataUpdate,this);
}
In DataBound versuchen wir, die DataSource-Eigenschaft über die Reflection-API abzurufen und dann einen Verweis auf die tatsächliche Datenquelle zurückzugeben. Da nun die Datenquelle bekannt ist, muss die Paging-Steuerung auch wissen, wie sie mit der Datenquelle umgeht. Um die Paging-Steuerung unabhängig von einer bestimmten Präsentationssteuerung zu machen, ist das Problem viel komplizierter. Wenn man die Paging-Steuerung jedoch von einer bestimmten Datenquelle abhängig macht, wird das Ziel einer flexiblen Paging-Steuerung zunichte gemacht. Wir müssen eine Plug-In-Architektur verwenden, um sicherzustellen, dass die Paging-Steuerung verschiedene Datenquellen verarbeiten kann, unabhängig davon, ob es sich um die von .NET bereitgestellte Datenquelle oder eine benutzerdefinierte Datenquelle handelt.
Um eine robuste, skalierbare, steckbare Architektur bereitzustellen, erstellen wir eine Lösung unter Verwendung des [GoF] Builder-Musters.
Abbildung 4:
Die IDataSourceAdapter-Schnittstelle definiert die grundlegendsten Elemente, die für die Paging-Steuerung zum Betrieb von Daten erforderlich sind, was einem „Plug“ entspricht.
publicinterface IDataSourceAdapter
{
int TotalCount{get;}
object GetPagedData(int start,int end);
}
Die TotalCount-Eigenschaft gibt die Gesamtzahl der in der Datenquelle enthaltenen Elemente vor der Verarbeitung der Daten zurück, während die GetPagedData-Methode eine Teilmenge der Originaldaten zurückgibt. Beispiel: Angenommen, die Datenquelle ist ein Array mit 20 Elementen, wird das Paging-Steuerelement angezeigt Wenn die Daten 10 Elemente pro Seite umfassen, besteht die Teilmenge der Elemente auf der ersten Seite aus den Array-Elementen 0–9 und die Teilmenge der Elemente auf der zweiten Seite aus den Array-Elementen 10–19. DataViewAdapter stellt einen DataView-Typ-Plug bereit:
interne Klasse DataViewAdapter:IDataSourceAdapter
{
private DataView _view;
interner DataViewAdapter (DataView-Ansicht)
{
_view = view;
}
public int TotalCount
{
get{return (_view == null) ? 0 : _view.Table.Rows.Count;}
}
öffentliches Objekt GetPagedData(int start, int end)
{
DataTable table = _view.Table.Clone();
for (int i = start;i<=end && i<= TotalCount;i++)
{
table.ImportRow(_view[i-1].Row);
}
Rückgabetabelle;
}
}
DataViewAdapter implementiert die GetPagedData-Methode von IDataSourceAdapter, die die ursprüngliche DataTable klont und die Daten in der ursprünglichen DataTable in die neue DataTable importiert. Die Sichtbarkeit dieser Klasse ist absichtlich auf „intern“ eingestellt, um Implementierungsdetails vor Webentwicklern zu verbergen und eine einfachere Schnittstelle über die Builder-Klasse bereitzustellen.
öffentliche abstrakte Klasse AdapterBuilder
{
privates Objekt _source;
private void CheckForNull()
{
if (_source == null) throw new NullReferenceException("Eine zulässige Datenquelle muss bereitgestellt werden");
}
öffentliche virtuelle Objektquelle
{
erhalten
{
CheckForNull();
return _source;}
Satz
{
_source = Wert;
CheckForNull();
}
}
öffentlicher abstrakter IDataSourceAdapter Adapter{get;}
}
Die abstrakte Klasse AdapterBuilder bietet eine besser verwaltbare Schnittstelle für den Typ IdataSourceAdapter. Aufgrund des erhöhten Abstraktionsniveaus müssen wir IdataSourceAdapter nicht mehr direkt verwenden. Gleichzeitig stellt AdapterBuilder auch Anweisungen zum Durchführen der Vorverarbeitung vor dem Paging von Daten bereit. Darüber hinaus macht dieser Builder auch die eigentliche Implementierungsklasse, z. B. DataViewAdapter, für Benutzer des Paging-Steuerelements transparent:
öffentliche Klasse DataTableAdapterBuilder:AdapterBuilder
{
privater DataViewAdapter _adapter;
privater DataViewAdapter ViewAdapter
{
erhalten
{
if (_adapter == null)
{
DataTable table = (DataTable)Source;
_adapter = new DataViewAdapter(table.DefaultView);
}
return _adapter;
}
}
öffentlicher IDataSourceAdapter-Adapter überschreiben
{
erhalten
{
return ViewAdapter;
}
}
}
öffentliche Klasse DataViewAdapterBuilder:AdapterBuilder
{
privater DataViewAdapter _adapter;
privater DataViewAdapter ViewAdapter
{
erhalten
{ // Verzögerte Instanziierung
if (_adapter == null)
{
_adapter = new DataViewAdapter((DataView)Source);
}
return _adapter;
}
}
öffentlicher IDataSourceAdapter-Adapter überschreiben
{
get{return ViewAdapter;}
}
}
Der DataView-Typ und der DataTable-Typ sind so eng miteinander verbunden, dass es sinnvoll sein kann, einen generischen DataAdapter zu erstellen. Tatsächlich reicht es aus, nur einen weiteren Konstruktor hinzuzufügen, der die DataTable verarbeitet. Wenn Benutzer für den Umgang mit einer DataTable unterschiedliche Funktionen benötigen, muss leider die gesamte Klasse ersetzt oder geerbt werden. Wenn wir einen neuen Builder erstellen, der denselben IdataSourceAdapter verwendet, hat der Benutzer mehr Freiheit bei der Auswahl der Implementierung des Adapters.
In der Paging-Steuerung wird die Suche nach der entsprechenden Builder-Klasse durch eine typsichere Sammlung abgeschlossen.
öffentliche Klasse AdapterCollection:DictionaryBase
{
privater String GetKey(Typ Schlüssel)
{
return key.FullName;
}
öffentliche AdapterCollection() {}
publicvoid Add(Typschlüssel,AdapterBuilder-Wert)
{
Dictionary.Add(GetKey(key),value);
}
publicbool Enthält (Typschlüssel)
{
return Dictionary.Contains(GetKey(key));
}
publicvoid Remove(Typschlüssel)
{
Dictionary.Remove(GetKey(key));
}
public AdapterBuilder this[Typschlüssel]
{
get{return (AdapterBuilder)Dictionary[GetKey(key)];}
set{Dictionary[GetKey(key)]=value;}
}
}
AdapterCollection basiert auf dem DataSource-Typ, und DataSource wird geschickt über BoundControl_DataBound eingeführt. Der hier verwendete Indexschlüssel ist die Type.FullName-Methode, die die Eindeutigkeit des Indexschlüssels jedes Typs gewährleistet. Gleichzeitig wird der AdapterCollection auch die Verantwortung zugewiesen, sicherzustellen, dass es für jeden Typ nur einen Builder gibt. Fügen Sie die Builder-Suche zur BoundControl_DataBound-Methode hinzu und die Ergebnisse lauten wie folgt:
public AdapterCollection Adapters
{
get{return _adapters;}
}
private bool HasParentControlCalledDataBinding
{
get{return _builder != null;}
}
private void BoundControl_DataBound(object sender,System.EventArgs e)
{
if (HasParentControlCalledDataBinding) return;
Typ type = sender.GetType();
_datasource = type.GetProperty("DataSource");
if (_datasource == null)
throw new NotSupportedException("Das Paging-Steuerelement erfordert, dass das Präsentationssteuerelement eine DataSource enthalten muss.");
Objektdaten = _datasource.GetGetMethod().Invoke(sender,null);
_builder = Adapters[data.GetType()];
if (_builder == null)
throw new NullReferenceException("Der entsprechende Adapter ist nicht installiert, um den folgenden Datenquellentyp zu verarbeiten: "+data.GetType());
_builder.Source = data;
ApplyDataSensitivityRules();
BindParent();
RaiseEvent(DataUpdate,this);
}
Die BoundControl_DataBound-Methode verwendet HasParentControlCalledDataBinding, um zu prüfen, ob der Builder erstellt wurde. Wenn dies der Fall ist, führt sie den Vorgang zum Suchen des entsprechenden Builders nicht mehr aus. Die Initialisierung der Adaptertabelle erfolgt im Konstruktor:
public Pager()
{
SelectedPager=new System.Web.UI.WebControls.Style();
UnselectedPager = new System.Web.UI.WebControls.Style();
_adapters = new AdapterCollection();
_adapters.Add(typeof(DataTable),new DataTableAdapterBuilder());
_adapters.Add(typeof(DataView),new DataViewAdapterBuilder());
}
Die letzte zu implementierende Methode ist BindParent, die zur Verarbeitung und Rückgabe von Daten verwendet wird.
private void BindParent()
{
_datasource.GetSetMethod().Invoke(BoundControl,
neues Objekt[]{_builder.Adapter.GetPagedData(StartRow,ResultsToShow*CurrentPage)});
}
Diese Methode ist sehr einfach, da die Datenverarbeitung tatsächlich vom Adapter durchgeführt wird. Nachdem dieser Vorgang abgeschlossen ist, verwenden wir erneut die Reflection-API, diesmal jedoch zum Festlegen der DataSource-Eigenschaft des Präsentationssteuerelements.
3. Interface-Design
Bisher sind die Kernfunktionen der Paging-Steuerung nahezu implementiert, mangels geeigneter Darstellungsmethoden wird die Paging-Steuerung jedoch keinen großen Nutzen bringen.
Um die Präsentationsmethode effektiv von der Programmlogik zu trennen, ist die Verwendung von Vorlagen, genauer gesagt die Verwendung der Itemplate-Schnittstelle, die beste Möglichkeit. Tatsächlich ist sich Microsoft der Leistungsfähigkeit von Vorlagen bewusst und verwendet sie fast überall, sogar im Seitenparser selbst. Leider sind Vorlagen kein so einfaches Konzept, wie manche Leute denken, und es dauert einige Zeit, das Wesentliche zu verstehen. Glücklicherweise gibt es in diesem Bereich viele Informationen, daher werde ich hier nicht ins Detail gehen. Zurück zur Seitensteuerung: Sie verfügt über vier Schaltflächen: Startseite, vorherige Seite, nächste Seite, letzte Seite und natürlich die Nummer jeder Seite. Die vier Navigationsschaltflächen werden aus der ImageButton-Klasse anstelle der LinkButton-Klasse ausgewählt. Aus professioneller Webdesign-Perspektive sind grafische Schaltflächen offensichtlich nützlicher als monotone Links.
public ImageButton FirstButton{get {return First;}}
public ImageButton LastButton{get {return Last;}}
public ImageButton PreviousButton{get {return Previous;}}
public ImageButton NextButton{get {return Next;}}
Seitenzahlen werden dynamisch erstellt, da sie von der Anzahl der Datensätze in der Datenquelle und der Anzahl der auf jeder Seite angezeigten Datensätze abhängen. Die Seitenzahl wird einem Panel hinzugefügt, und Webdesigner können das Panel verwenden, um anzugeben, wo die Seitenzahl angezeigt werden soll. Der Prozess zum Erstellen von Seitenzahlen wird später ausführlich besprochen. Jetzt müssen wir eine Vorlage für das Paging-Steuerelement bereitstellen, damit Benutzer das Erscheinungsbild des Paging-Steuerelements anpassen können.
[Vorlagencontainer(typeof(LayoutContainer))]
öffentliches ITemplate-Layout
{
get{return (_layout;}
set{_layout =value;}
}
öffentliche Klasse LayoutContainer:Control,INamingContainer
{
publicLayoutContainer()
{this.ID = "Seite";}
}
Die LayoutContainer-Klasse stellt einen Container für Vorlagen bereit. Im Allgemeinen ist es immer sinnvoll, dem Vorlagencontainer eine benutzerdefinierte ID hinzuzufügen, um Probleme bei der Verarbeitung von Ereignissen und Seitenaufrufen zu vermeiden. Das folgende UML-Diagramm beschreibt den Darstellungsmechanismus der Paging-Steuerung.
Abbildung 5
Der erste Schritt beim Erstellen einer Vorlage besteht darin, das Layout auf der ASPX-Seite zu definieren:
<LAYOUT>
<asp:ImageButton id="First" Runat="server" imageUrl="play2L_dis.gif"
AlternateText="Home"></asp:ImageButton>
<asp:ImageButton id="Previous" Runat="server" imageUrl="play2L.gif"
AlternateText="Vorherige Seite"></asp:ImageButton>
<asp:ImageButton id="Next" Runat="server" imageUrl="play2.gif"
AlternateText="Nächste Seite"></asp:ImageButton>
<asp:ImageButton id="Last" Runat="server" imageUrl="play2_dis.gif"
AlternateText="Letzte Seite"></asp:ImageButton>
<asp:Panel id="Pager" Runat="server"></asp:Panel>
</LAYOUT>
Dieses Layoutbeispiel enthält keine Formatelemente wie Tabellen usw. Natürlich können (und sollten) tatsächliche Anwendungen Formatelemente hinzufügen, weitere Anweisungen finden Sie später.
Die Itemplate-Schnittstelle bietet nur eine Methode InstantiateIn, die die Vorlage analysiert und den Container bindet.
private void InstantiateTemplate()
{
_container = new LayoutContainer();
Layout.InstantiateIn(_container);
First = (ImageButton)_container.FindControl("First");
Previous = (ImageButton)_container.FindControl("Previous");
Next = (ImageButton)_container.FindControl("Next");
Last = (ImageButton)_container.FindControl("Last");
Holder = (Panel)_container.FindControl("Pager");
this.First.Click += new System.Web.UI.ImageClickEventHandler(this.First_Click);
this.Last.Click += new System.Web.UI.ImageClickEventHandler(this.Last_Click);
this.Next.Click += new System.Web.UI.ImageClickEventHandler(this.Next_Click);
this.Previous.Click += new System.Web.UI.ImageClickEventHandler(this.Previous_Click);
}
Die erste Aufgabe der InstatiateTemplate-Methode des Steuerelements besteht darin, die Vorlage zu instanziieren, d. h. Layout.InstantiateIn(_container) aufzurufen. Ein Container ist eigentlich eine Art Steuerelement und seine Verwendung ähnelt der anderer Steuerelemente. Die InstantiateTemplate-Methode verwendet diese Funktion, um die vier Navigationsschaltflächen und das Panel zu finden, in dem die Seitenzahl gespeichert ist. Navigationsschaltflächen werden anhand ihrer IDs gefunden. Dies ist eine kleine Einschränkung bei Paging-Steuerelementen: Navigationsschaltflächen müssen bestimmte IDs haben, nämlich „Erste“, „Vorherige“, „Weiter“ und „Letzte“. Außerdem muss die ID des Panels „Pager“ lauten, andernfalls ist dies nicht der Fall gefunden. Leider scheint dies der bessere Ansatz für den von uns gewählten Präsentationsmechanismus zu sein. Bei ordnungsgemäßer Dokumentation wird diese geringfügige Einschränkung jedoch hoffentlich keine Probleme verursachen. Eine andere Alternative besteht darin, jede Schaltfläche von der ImageButton-Klasse erben zu lassen und so einen neuen Typ zu definieren. Da jede Schaltfläche einen anderen Typ hat, kann eine rekursive Suche im Container implementiert werden, um verschiedene spezifische Schaltflächen zu finden, wodurch die Verwendung der Schaltflächen-ID überflüssig wird Attribut.
Nachdem Sie die vier Schaltflächen gefunden haben, binden Sie die entsprechenden Ereignishandler daran. Hier muss eine wichtige Entscheidung getroffen werden, nämlich wann InstantiateTemplate aufgerufen werden soll. Im Allgemeinen sollte diese Art von Methode in der Methode „CreateChildControls“ aufgerufen werden, da der Hauptzweck der Methode „CreateChildControls“ darin besteht, untergeordnete Steuerelemente zu erstellen. Da das Paging-Steuerelement seine untergeordneten Steuerelemente niemals ändert, benötigt es nicht die von CreateChildControls bereitgestellte Funktionalität, um den Anzeigestatus basierend auf einem Ereignis zu ändern. Je schneller das untergeordnete Steuerelement angezeigt wird, desto besser. Daher ist der ideale Ort zum Aufrufen der InstantiateTemplate-Methode das OnInit-Ereignis.
protected override void OnInit(EventArgs e)
{
_boundcontrol = Parent.FindControl(BindToControl);
BoundControl.DataBinding += new EventHandler(BoundControl_DataBound);
InstantiateTemplate();
Controls.Add(_container);
base.OnInit(e);
}
Neben dem Aufruf der InstantiateTemplate-Methode hat die OnInit-Methode noch eine weitere wichtige Aufgabe: das Hinzufügen des Containers zum Paging-Steuerelement. Wenn Sie den Container nicht zur Steuerelementsammlung des Pagers hinzufügen, wird die Vorlage nicht angezeigt, da die Render-Methode nie aufgerufen wird.
Vorlagen können auch programmgesteuert definiert werden, indem die Itemplate-Schnittstelle implementiert wird. Diese Funktion ist nicht nur eine Maßnahme zur Erhöhung der Flexibilität, sondern kann auch eine Standardvorlage zur Verwendung bereitstellen, wenn der Benutzer keine Vorlage über eine ASPX-Seite bereitstellt.
öffentliche Klasse DefaultPagerLayout:ITemplate
{
privater ImageButton Weiter;
privater ImageButton First;
privater ImageButton Last;
privater ImageButton Previous;
privater Panel Pager;
public DefaultPagerLayout()
{
Next = new ImageButton();
First = new ImageButton();
Last = new ImageButton();
Previous = new ImageButton();
Pager = new Panel();
Next.ID="Next"; Next.AlternateText="Next page";Next.ImageUrl="play2.gif";
First.ID="First"; First.AlternateText="Home";First.ImageUrl="play2L_dis.gif";
Last.ID = "Last"; Last.AlternateText = "Letzte Seite";
Previous.ID="Previous"; Previous.AlternateText="Vorherige Seite";Previous.ImageUrl="play2L.gif";
Pager.ID="Pager";
}
public void InstantiateIn(Kontrollkontrolle)
{
control.Controls.Clear();
Tabelle table = new Table();
table.BorderWidth = Unit.Pixel(0);
table.CellSpacing= 1;
table.CellPadding =0;
TableRow row = new TableRow();
row.VerticalAlign = VerticalAlign.Top;
table.Rows.Add(row);
TableCell cell = new TableCell();
cell.HorizontalAlign = HorizontalAlign.Right;
cell.VerticalAlign = VerticalAlign.Middle;
cell.Controls.Add(First);
cell.Controls.Add(Previous);
row.Cells.Add(cell);
cell = new TableCell();
cell.HorizontalAlign= HorizontalAlign.Center;
cell.Controls.Add(Pager);
row.Cells.Add(cell);
cell = new TableCell();
cell.VerticalAlign = VerticalAlign.Middle;
cell.Controls.Add(Next);
cell.Controls.Add(Last);
row.Cells.Add(cell);
control.Controls.Add(table);
}
}
DefaultPagerLayout stellt alle Navigationselemente programmgesteuert bereit und fügt sie der ASPX-Seite hinzu, aber dieses Mal sind die Navigationselemente mit Standard-HTML-Tabellen formatiert. Wenn der Benutzer nun keine Präsentationsvorlage bereitstellt, stellt das Programm automatisch eine Standardvorlage bereit.
[TemplateContainer(typeof(LayoutContainer))]
öffentliches ITemplate-Layout
{
get{return (_layout == null)? new DefaultPagerLayout():_layout;}
set{_layout =value;}
}
Werfen wir einen Blick auf den Prozess der Generierung der einzelnen Seitenzahlen. Die Paging-Steuerung muss zunächst einige Eigenschaftswerte ermitteln und anhand dieser Eigenschaftswerte bestimmen, wie viele verschiedene Seitenzahlen generiert werden sollen.
public int CurrentPage
{
erhalten
{
string cur = (string)ViewState["CurrentPage"];
return (cur == string.Empty || cur ==null)?
}
Satz
{
ViewState["CurrentPage"] = value.ToString();}
}
public int PagersToShow
{
get{return _results;}
set{_results = value;}
}
public int ResultsToShow
{
get{return _resultsperpage;}
set{_resultsperpage = value;}
}
CurrentPage speichert tatsächlich die aktuelle Seite im ViewState der Seitenzahl. Die durch die PagersToShow-Methode definierten Eigenschaften ermöglichen es dem Benutzer, anzugeben, wie viele Seiten angezeigt werden sollen, während die durch ResultsToShow definierten Eigenschaften es dem Benutzer ermöglichen, anzugeben, auf wie vielen Datensätzen angezeigt werden soll pro Seite. Der Standardwert ist 10.
NumberofPagersToGenerate gibt die aktuelle Anzahl der Seitenzahlen zurück, die generiert werden sollen.
private int PagerSequence
{
erhalten
{
Geben Sie Convert.ToInt32 zurück
(Math.Ceiling((double)CurrentPage/(double)PagersToShow));}
}
private int NumberOfPagersToGenerate
{
get{return PagerSequence*PagersToShow;}
}
private int TotalPagesToShow
{
get{return Convert.ToInt32(Math.Ceiling((double)TotalResults/(double)_resultsperpage));}
}
public int TotalResults
{
get{return _builder.Adapter.TotalCount;}
}
Die TotalPagesToShow-Methode gibt die Gesamtzahl der anzuzeigenden Seiten zurück, angepasst durch die voreingestellte ResultsToShow-Eigenschaft des Benutzers.
Obwohl ASP.NET einige Standardstile definiert, sind diese für Benutzer von Paging-Steuerelementen möglicherweise nicht sehr praktisch. Benutzer können das Erscheinungsbild des Paging-Steuerelements durch benutzerdefinierte Stile anpassen.
public Style UnSelectedPagerStyle {get {return UnselectedPager;}}
public Style SelectedPagerStyle {get {return SelectedPager;}}
UnSelectedPagerStyle stellt den Stil bereit, der verwendet wird, wenn die Seitenzahl nicht ausgewählt ist, und SelectedPagerStyle stellt den Stil bereit, der verwendet wird, wenn die Seitenzahl ausgewählt ist.
private void GeneratePagers(WebControl-Steuerung)
{
control.Controls.Clear();
int pager = (PagerSequence-1)* PagersToShow +1;
for (;pager<=NumberOfPagersToGenerate && pager<=TotalPagesToShow;pager++)
{
LinkButton link = new LinkButton();
link.Text = pager.ToString();
link.ID = pager.ToString();
link.Click += new EventHandler(this.Pager_Click);
if (link.ID.Equals(CurrentPage.ToString()))
link.MergeStyle(SelectedPagerStyle);
anders
link.MergeStyle(UnSelectedPagerStyle);
control.Controls.Add(link);
control.Controls.Add(new LiteralControl(" "));
}
}
private void GeneratePagers()
{
GeneratePagers(Holder);
}
Die GeneratePagers-Methode erstellt dynamisch alle Seitenzahlen, bei denen es sich um Schaltflächen vom Typ LinkButton handelt. Die Beschriftungs- und ID-Attribute jeder Seitennummer werden über eine Schleife zugewiesen und gleichzeitig wird das Klickereignis an den entsprechenden Ereignishandler gebunden. Abschließend wird die Seitenzahl einem Container-Steuerelement hinzugefügt – in diesem Fall einem Panel-Objekt. Die Button-ID dient der Identifizierung, welcher Button das Klickereignis ausgelöst hat. Das Folgende ist die Definition des Ereignishandlers:
private void Pager_Click(object sender, System.EventArgs e)
{
LinkButton-Schaltfläche = (LinkButton) Absender;
CurrentPage = int.Parse(button.ID);
RaiseEvent(PageChanged, this,new PageChangedEventArgs(CurrentPage,PagedEventInvoker.Pager));
Aktualisieren();
}
private void Next_Click(object sender, System.Web.UI.ImageClickEventArgs e)
{
if (CurrentPage<TotalPagesToShow)
CurrentPage++;
RaiseEvent(PageChanged, this,new PageChangedEventArgs(CurrentPage,PagedEventInvoker.Next));
Aktualisieren();
}
private void Previous_Click(object sender, System.Web.UI.ImageClickEventArgs e)
{
if (AktuelleSeite > 1)
CurrentPage--;
RaiseEvent(PageChanged, this,new PageChangedEventArgs(CurrentPage,PagedEventInvoker.Previous));
Aktualisieren();
}
private void First_Click(object sender, System.Web.UI.ImageClickEventArgs e)
{
Aktuelle Seite = 1;
RaiseEvent(PageChanged, this,new PageChangedEventArgs(CurrentPage,PagedEventInvoker.First));
Aktualisieren();
}
private void Last_Click(object sender, System.Web.UI.ImageClickEventArgs e)
{
CurrentPage = TotalPagesToShow;
RaiseEvent(PageChanged, this,new PageChangedEventArgs(CurrentPage,PagedEventInvoker.Last));
Aktualisieren();
}
Diese Ereignishandler ähneln sich darin, dass sie zuerst die aktuelle Seite des Paging-Steuerelements ändern und dann das gebundene Steuerelement aktualisieren.
privates void Update()
{
if (!HasParentControlCalledDataBinding) return;
ApplyDataSensitivityRules();
BindParent();
BoundControl.DataBind();
}
Zunächst prüft die Paging-Steuerung, ob die notwendigen Adapter initialisiert wurden, indem sie die Methode HasParentControlCalledDataBinding aufruft. Wenn ja, wenden Sie die zuvor genannten Regeln zum automatischen Anpassen von Steuerelementen basierend auf den Datenanzeigebedingungen auf das aktuelle Steuerelement an. Diese Regeln bewirken, dass sich das Paging-Steuerelement je nach den unterschiedlichen Bedingungen der Daten im BoundControl unterschiedlich verhält. Obwohl diese Regeln intern durch die Paging-Steuerung gesteuert werden, können sie bei Bedarf mithilfe des [GoF]-Statusmodus problemlos aus der Steuerung verschoben werden.
öffentlicher bool IsDataSensitive
{
get{return _isdatasensitive;}
set{_isdatasensitive = value;}
}
private bool IsPagerVisible
{
get{return (TotalPagesToShow != 1) && IsDataSensitive;}
}
private bool IsPreviousVisible
{
erhalten
{
return (!IsDataSensitive)? true:
(AktuelleSeite != 1);
}
}
private bool IsNextVisible
{
erhalten
{
return (!IsDataSensitive)? true:
(CurrentPage != TotalPagesToShow);
}
}
private void ApplyDataSensitivityRules()
{
FirstButton.Visible = IsPreviousVisible;
PreviousButton.Visible = IsPreviousVisible;
LastButton.Visible = IsNextVisible;
NextButton.Visible = IsNextVisible;
if (IsPagerVisible) GeneratePagers();
}
Die ApplyDataSensitivityRules-Methode implementiert vordefinierte Regeln wie IsPagerVisible, IsPreviousVisible und IsNextVisible. Standardmäßig sind diese Regeln für das Paging-Steuerelement aktiviert, der Benutzer kann sie jedoch deaktivieren, indem er die IsDataSensitive-Eigenschaft festlegt.
Bisher wurde der Anzeigeteil der Paging-Steuerung grundsätzlich entworfen. Die letzte verbleibende Abschlussarbeit besteht darin, mehrere Ereignishandler bereitzustellen, damit Benutzer die erforderlichen Anpassungen vornehmen können, wenn verschiedene Paging-Kontrollereignisse auftreten.
öffentlicher Delegate void PageDelegate(object sender,PageChangedEventArgs e);
public enum PagedEventInvoker{Next,Previous,First,Last,Pager}
öffentliche Klasse PageChangedEventArgs:EventArgs
{
private int neue Seite;
privater Enum-Aufrufer;
public PageChangedEventArgs(int newpage):base()
{
this.newpage = neue Seite;
}
public PageChangedEventArgs(int newpage,PagedEventInvoker invoker)
{
this.newpage = neue Seite;
this.invoker = Aufrufer;
}
public int NewPage {get{return newpage;}}
public Enum EventInvoker{get{return invoker;}}
}
Da das Paging-Steuerelement benutzerdefinierte Ereignisparameter zurückgeben muss, definieren wir eine dedizierte PageChangedEventArgs-Klasse. Die PageChangedEventArgs-Klasse gibt den PagedEventInvoker-Typ zurück, der ein Enumerator der Steuerelemente ist, die das Ereignis auslösen können. Um benutzerdefinierte Ereignisparameter zu verarbeiten, definieren wir einen neuen Delegaten, PageDelegate. Das Ereignis ist in der folgenden Form definiert:
öffentliches Ereignis PageDelegate PageChanged;
öffentliches Ereignis EventHandler DataUpdate;
Wenn ein Ereignis keinen entsprechenden Ereignis-Listener hat, löst ASP.NET eine Ausnahme aus. Das Paging-Steuerelement definiert die folgenden RaiseEvent-Methoden.
private void RaiseEvent(EventHandler e,object sender)
{
this.RaiseEvent(e,this,null);
}
private void RaiseEvent(EventHandler e,object sender, PageChangedEventArgs args)
{
if(e!=null)
{
e(sender,arg);
}
}
private void RaiseEvent(PageDelegate e,object sender)
{
this.RaiseEvent(e,this,null);
}
private void RaiseEvent(PageDelegate e,object sender, PageChangedEventArgs args)
{
if(e!=null)
{
e(sender,arg);
}
}
Event -Handler können jetzt Ereignisse auslösen, indem sie einzelne RaiseEvent -Methoden aufrufen.
4. Anwendungsbeispiele
an diesem Punkt wurde das Design der Paging -Kontrolle abgeschlossen und kann offiziell verwendet werden. Um die Paging -Steuerung zu verwenden, binden Sie einfach an eine Präsentationskontrolle.
<ASP: Repeater ID = "Repeater" Runat = "Server">
<ItemTemplate>
Spalte 1:
<%# Convert.toString (Databinder.eval (Container.Dataitem, "Column1")%>
<br>
Spalte 2:
<%# Convert.toString (Databinder.eval (Container.Dataitem, "Column2")%>
<br>
Spalte 3:
<%# Convert.toString (Databinder.eval (Container.Dataitem, "Column3")%>
<br>
<hr>
</ItemTemplate>
</ASP: Repeater >
< cc1: pager id = "pager" resultstoShow = "2" runat = "server" BindTocontrol = "Repeater" >
< SELECTEDPAGERSYLE BACKOROR = "YELE" />
</CC1: Pager >
Die obige ASPX -Seite bindet die Paging -Steuerung an eine Repeater -Steuerung, legt die Anzahl der auf jeder Seite angezeigten Datensätze auf 2 fest, die Farbe der ausgewählten Seitenzahl ist gelb und verwendet das Standardlayout Wie in Abbildung 1 gezeigt. Im Folgenden finden Sie ein weiteres Beispiel, das die Paging -Steuerung an ein Datagrid bindet, wie in Abbildung 2 gezeigt.
< ASP: DataGrid ID = "Grid" runat = "Server" ></ASP: DataGrid >
< cc1: pager id = "pagerGrid" resultstoShow = "2" runat = "server" BindTocontrol = "Grid" > >
< SELECTEDPAGERSYLE BACKOROR = "Red" ></SelectedPagerSyle >
< Layout >
< ASP: ImageButton id = "First" Runat = "Server" ImageRl = "Play2l_Dis.gif" AlternatETEXT = "HOME" ></ASP: ImageButton >
< ASP: ImageButton id = "vorher" runat = "server" imageurl = "play2l.gif" alternatEtext = "vorherige Seite" ></ASP: ImageButton >
< ASP: ImageButton id = "Next" runat = "server" imageurl = "play2.gif" alternateText = "nächste Seite" ></ASP: ImageButton >
< ASP: ImageButton id = "Last" runat = "server" imageurl = "play2_dis.gif" alternatetext = "Last Page" ></ASP: ImageButton >
< ASP: Panel ID = "Pager" Runat = "Server" ></ASP: Panel >
</Layout >
</CC1: Pager >
Der Test zeigt, dass die Paging -Steuerung nicht von bestimmten Präsentationssteuerungen abhängt. das vollständige Beispiel.
Obwohl das Erlernen von benutzerdefinierten Websteuerungen keine leichte Aufgabe ist, sind die Vorteile dieser Fähigkeit selbstverständlich. Zeiten.
http://www.cnblogs.com/niit007/archive/2006/08/13/475501.html