Websiteentwicklung mit ASP.NET/ Webseiten erstellen

Aus Wikibooks

Webseiten erstellen[Bearbeiten]

Funktionsweise einer Webseite[Bearbeiten]

Nach dem Hallo-Welt Beispiel wird es Zeit, systematisch die Funktionsweise einer Webseite zu betrachten. Die erste Webseite soll einen Zähler enthalten, der bei jedem Aufruf der Seite erhöht wird. Zudem soll ein Label-Steuerelement darüber Auskunft geben, welche Funktionen aufgerufen wurden.

Dazu startet man am besten eine neue Webseite (z.B. im Visual Web Developer mit der Vorlage ASP.NET WebSite). Mit anderen Worten: Es sollten zwei Dateien vorhanden sein, nämlich die Datei default.aspx mit

<%@ Page Language="C#" CodeFile="Default.aspx.cs" Inherits="_Default" %>
 
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>Erste Webseite</title>
</head>
<body>
    <form id="ersteForm" runat="server">
        <asp:Label id="LabelAusgabe" runat="server" />
        <br />
        <asp:Label id="LabelZaehler" runat="server" />
    </form>
</body>
</html>

sowie die zugehörige Codedatei default.aspx.cs

 using System;
 using System.Web;
 using System.Web.UI.WebControls; 
 
 public partial class _Default : System.Web.UI.Page 
 {
     int naiver_zaehler = 0; 
  
     protected void Page_Load(object sender, EventArgs e)
     {
        naiver_zaehler++;
        LabelAusgabe.Text += "Page_Load wird ausgeführt.";
        LabelZaehler.Text = "zähler="+naiver_zaehler;
     }
 }

Die Methode Page_Load ist die Ereignisbehandlung für das Laden einer Webseite.

Führt man dieses Beispiel aus und ruft wiederholt in einem Webbrowser die Seite default.aspx so ist das Ergebnis für so manchen C++ oder C# Programmierer ohne Weberfahrung überraschend, wird doch immerfort

Page_Load wird ausgeführt. zähler=1

ausgegeben. Des Rätsels Lösung ist dabei überraschend einfach: Das von System.Web.UI.Page geerbte Objekt _Default hat einen Lebenszyklus, der mit genau einem Seitenaufruf übereinstimmt. ASP.NET bietet hierfür einen Mechanismus, den Lebenszyklus einer Webseite näher zu untersuchen, nämlich das Tracing.

Um das Tracing zu aktivieren, muss die Page-Direktive in default.aspx abgeändert werden zu

<%@ Page Language="C#" CodeFile="Default.aspx.cs" Trace="true" Inherits="_Default" %>

Der nächste Aufruf der default.aspx liefert nun zahlreiche Informationen über die Webseite und ihren Zyklus:

Tracing zum Lebenszyklus

Etwas vereinfacht besteht der Lebenszyklus aus folgenden Ereignissen:

  • Init: das mit der Webseite verbundene Objekt wird Initalisiert, dieses Ereignis kann für die Initalisierung eigener interner Variablen benutzt werden.
  • Load: dieses Ereignis wurde bisher schon mit der Methode Page_Load benutzt. Üblicherweise werden mit diesem Ereignis die Steuerelemente einer Seite konfiguriert.
  • PreRender: der Ladevorgang der Seite ist abgeschlossen, aber das Rendern (d.h. das Übersetzen in HTML und Skriptsprachen) wurde noch nicht begonnen.
  • PreRenderComplete: es wird mit dem Rendern der Seite begonnen.
  • Ist die Seite vollständig gerendert, so wird sie vom Webserver versendet. Dabei werden Ausgabe-Caches benutzt, die mit ASP.NET kontrolliert werden können. Vorerst sind dieses Details jedoch weniger interessant.
  • Unload: das mit der Webseite verbundene Objekt wird entladen

Dieses Modell ist an das HTTP-Protokoll angelehnt. Das HTTP-Protokoll ist zustandslos, das heißt nachdem eine Webseite aufgerufen wurde "vergisst" der Server alle Informationen über den Client – in dem vorherigen Beispiel, welchen Wert der Zähler hatte. Der Programmierer muss sich also darum kümmern, wie die Informationen (Welcher Benutzer? – Welche Einstellungen? – …) zwischen verschiedenen Seitenaufrufen erhalten bleiben.

Glücklicherweise sind in ASP.NET gleich mehrere solche Verfahren eingebaut, so dass sich der Programmierer nur um das Notwendige kümmern muss. Die wichtigsten Verfahren sind

  • die Session-Eigenschaft einer Seite (von Page geerbt). Hier können Informationen über einen Benutzer gespeichert werden.
  • die Application-Eigenschaft einer Seite (auch von Page geerbt). Hier können Informationen, die für die Webanwendung als ganzes wichtig sind gespeichert werden.
  • und schließlich noch das Schreiben und Lesen von Cookies. Hierfür benutzt man die Request und die Response- Eigenschaften einer Webseite.

Damit das Beispiel nun richtig funktioniert, kann man beispielsweise die Session-Eigenschaft benutzen: Die abgeänderte 'default.aspx.cs' ist dann

 using System;
 using System.Web;
 using System.Web.UI.WebControls;
 
 public partial class _Default : System.Web.UI.Page
 {
     int zaehler = 0;
     
     protected void Page_Load(object sender, EventArgs e)
     {
         if (Session["zaehler"] != null)              // wurde "zaehler" schon benutzt?
         {
             zaehler = (int)(Session["zaehler"]);     
         }
         zaehler++;
         LabelAusgabe.Text += "Page_Load wird ausgeführt.";
         LabelZaehler.Text = "zähler=" + zaehler;
         Session["zaehler"] = zaehler;               // "zaehler" in Session speichern
     }
 }

Oft kann man sich diesen Aufwand sparen, indem die Informationen in den Steuerelementen einer Seite hinterlegt werden. Diese Verfahren ist nicht neu in ASP.NET, sondern wird in der Webprogrammierung schon lange benutzt. Ist ein solches Steuerelement versteckt so spricht man auch von "hidden fields".

Webseiten gestalten[Bearbeiten]

Bisher wurden eher nebenläufig folgende Zutaten zu einer ASP.NET Webseite vorgestellt:

Layout und Design[Bearbeiten]

Das Layout der Seite wird mit HTML oder XHTML festgelegt, üblicherweise trägt eine solche Datei die Endung .aspx. Zum Design der Seite können dabei die üblichen Hilfsmittel CSS, JavaScript und andere verwendet werden.

Zusätzlich zu den Gestaltungsmöglichen von HTML können Steuerelemente (Controls) von ASP.NET benutzt werden. Viele gewöhnliche HTML oder XHTML Steuerelemente werden durch Hinzufügen des Attributs runat="server" so markiert, dass diese dynamisch auf dem Server gerendet werden, für ASP.NET Steuerelemente ist dies obligatorisch:

<asp:Steuerelement [Attribute...] runat="server"> Inhalt </asp:Steuerelement> 
<asp:Label id="LabelZaehler" runat="server" />  <!--Beispiel ASP.NET Steuerelement--> 
<div id="DivGerendert" runat="server"></div>    <!--Beispiel für HTML als Steuerelement--> 


Direktiven steuern das Kompilerverhalten und setzten Regeln für die serverseite Ausführung fest. Eine Direktive hat immer die Form

<%@ Direktive [Attribute…] %>
<%@ page Language="VB" %>  <!--Beispiel-->


Zudem ist es möglich Programmcode auch direkt in die Webseite als (Inline-)Codeblock einzubinden; die Syntax dieser Blöcke ist an die Code-Blöcke von ASP angelehnt:

<% hier Code ... %>

<!-- Beispiel -->
<%
  Response.Write("<p>Hallo, es ist " + DateTime.Now.TimeOfDay + " Uhr</p>");
%>

Inline-Codeblöcke sind mit größter Vorsicht zu genießen, zerstören sie doch die Trennung von Layout und Code! Am ehesten sind sie als Kompromiss an Programmierer aus den PHP- und ASP-Lagern anzusehen.

Der Vollständigkeit halber sollte man die Blöcke, die für die Steuerelemente mit Datenbindung verwendet werden, nicht vergessen. Diese werden im Kapitel über Steuerelemente ausführlich erklärt; ihre Syntax:

<%# Datenbindung %>
<asp:label text='<%# MeinObjekt.Text %>' runat="server"/><!-- Beispiel -->

Programmcode[Bearbeiten]

Der Programmcode wird üblicherweise in einer Datei hinterlegt, deren Name sich aus dem der Seite und einer erweiterten Dateiendung für die jeweilige Programmiersprache zusammensetzt. So heisst die Datei für den Programmcode zu default.aspx, nach dieser Konvention, default.aspx.cs oder default.aspx.vb. Von dieser Namenskonvention machen unter anderem Visual Studio und Visual Web Developer Gebrauch.

Ereignisse[Bearbeiten]

Ereignisse werden in C# mit Delegaten (d. h. verallgemeinerten "Zeigern" auf Funktionen) und durch das Hinzufügen von passenden Funktionen für die Ereignisbehandlung realisiert. Ein Objekt, das Ereignisse zur Verfügung stellt, sieht ungefähr aus wie:

    public delegate void EreignisHandler(int Irgendwas); //Prototyp des Delegaten (= Zeiger auf Funktion)
 
    public class ObjMitEreignis    // Dieses Objekt soll ein Ereignis 
    {                              // "OnMeinEreignis" haben
        public ObjMitEreignis()
        {
            // ... 
        }
         
        // Ereignis definieren
        <strong>public event EreignisHandler MeinEreignis</strong>;
 
        private void DummyCode()
        {
            // ... (Anwendung)
 
            // nun das Ereignis "abschicken"
            if (MeinEreignis!= null)
                MeinEreignis(1234);
        }
    }

Die Benachrichtigung zu einem Ereignis wird also einfach abgeschickt, indem der Delegat wie eine normale Funktion aufgerufen wird. Ein kleiner Fallstrick besteht: der Delegat kann durchaus null sein! Neben Objekten, die Nachrichten zu Ereignissen abschicken, gibt es natürlich auch Objekte, die auf diese hören. Für das obere Beispiel:

 // Auf Ereignisse reagieren 
 
 // objMitEreignis soll initalisiert sein...
 ObjMitEreignis objMitEreignis;
 
 public void Init()
 {
     <strong>objMitEreignis.MeinEreignis += new EreignisHandler(OnMeinEreignis)</strong>;
 }
 
 public void OnMeinEreignis(int Irgendwas)
 {
     Console.Write("Ereignis MeinEreignis eingetreten!");
 }

Der Empfänger "registriert" sich beim Sender, indem er mit dem Additionsoperator (+=) seinen Handler zum Ereignis hinzufügt.

Für andere Programmiersprachen sind ähnliche Konstrukte vorhanden. Interessant dabei: die Ereignisse sind keine Funktion einer Programmiersprache, sondern Teil des CLR-Ausführungssystems, also ein Teil der .NET Architektur. Der Sender könnte durchaus in Nemerle geschrieben sein, der Empfänger in VB.NET.

Es ist nun ein kleiner Schritt von Ereignissen im Allgemeinen zu den speziellen Ereignissen im ASP.NET. Im folgenden Beispiel ist eine Dropdown-Liste enthalten, der Anwender kann mit dieser eine Nachricht auswählen (siehe Bild). In ASP.NET wird das "Registrieren" beim Sender vereinfacht, die Steuerelemente stellen hierfür Attribute zur Verfügung (hier: OnSelectedIndexChanged).

<%@ Page Language="C#" CodeFile="Default.aspx.cs" Inherits="_Default" %>
 
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>Ereignisse</title>
</head>
<body>
    <form id="ereignisseForm" runat="server">
        Dein Land: 
        <asp:DropDownList id="DropDLOptionen" 
            OnSelectedIndexChanged="DropDLOptionen_SelIdxChanged"
            AutoPostBack="true"
            runat="server" />  
        <br />
        <asp:Label id="LabelHinweis" runat="server"></asp:Label>
    </form>
</body>
</html>

Programmcode:

 using System;
 using System.Web;
 using System.Web.UI.WebControls; 
 
 public partial class _Default : System.Web.UI.Page
 {
     string[] laender = {"England", "Deutschland", "Österreich", "Frankreich"};
     string[] hinweise = { "Hello!", "Hallo.", "Servus!", "Bon jour." }; 
 
     protected void Page_Load(object sender, EventArgs e)
     {
         LabelHinweis.Visible = false;
         if (!IsPostBack)
         {
             DropDLOptionen.DataSource = laender;
             DropDLOptionen.DataBind();
         }
     }
 
     protected void <strong>DropDLOptionen_SelIdxChanged</strong>(object sender, EventArgs e)
     {
         int selidx = DropDLOptionen.SelectedIndex;
         LabelHinweis.Visible = true;
         LabelHinweis.Text = hinweise[selidx];
     }
 }
Ereignisbehandlung für DropDown-Liste

Ein Blick auf den erzeugten HTML-Code bringt eine Überraschung mit sich: beim Rendern der Seite wurde von ASP.NET selbständig Script-Code eingefügt, hierfür ist das Attribut AutoPostBack verantwortlich. Ändert ein Anwender die Auswahl in der Dropdown-Liste so sorgt der Script-Code dafür, dass der Webbrowser die Webseite erneut aufruft. Die neue Auswahl wird dabei als POST-Datensatz an den Server geschickt, womit wiederum der Ereignishandler aufgerufen wird.

Wird eine Seite geladen, sollte man prüfen, ob es sich um den ersten Aufruf einer Seite handelt oder ob die Steuerelemente der Seite bereits initialisiert sind. Dazu benutzt man die IsPostBack-Eigenschaft, die bei einem Aufruf der Webseite mit POST-Daten true annimmt.