Share via


Dr. GUI .NET #8

 

20. September 2002

Zusammenfassung: Der gute Arzt verfolgt seine Arbeit an Conways Game of Life aus dem letzten Monat, nur dieses Mal entwickelt er das Spiel als Microsoft ASP.NET Anwendung, mit ein paar strategischen clientseitigen Microsoft JScript-Methoden. (14 gedruckte Seiten)

Inhalte

Einführung
Wo wir waren; Wohin wir gehen
Etwas Altes, ...
Etwas Neues, ...
Etwas geliehen, und...
Etwas Blaues!
Entwurfsoptionen
Versuch es doch mal!
Was wir getan haben; Was kommt als nächstes

Teilen Sie uns und der Welt mit, was Sie über diesen Artikel auf dem Dr. GUI .NET Message Board halten. Sehen Sie sich die Beispiele an, die als Microsoft® ASP.NET-Anwendungen mit Quellcode ausgeführt werden, unter: http://drgui.coldrooster.com/DrGUIdotNet/8/.

Einführung

Willkommen zurück zum neunten .NET Framework-Programmierartikel von Dr. GUI. (Die ersten acht waren zu Ehren .NET Framework Arrayelementnummerierung, die bei 0 statt eins beginnt, als "Dr. GUI .NET #0" bis "Dr. GUI .NET #7" bezeichnet.) Wenn Sie nach den früheren Artikeln suchen, lesen Sie die Dr. GUI .NET-Startseite.

Dr. GUI hofft, dass Sie am Message Board teilnehmen. Es gibt bereits einige gute Diskussionen dort, aber sie werden besser sein, wenn Sie mitmachen! Jeder kann die Nachrichten lesen, obwohl Sie einen Passport benötigen, um Sich zu authentifizieren, um zu posten. Machen Sie sich keine Sorgen – es ist einfach, einen Passport einzurichten, und Sie können ihn jedem Ihrer E-Mail-Konten zuordnen.

Wo wir waren; Wohin wir gehen

Zuletzt haben wir Game of Life von Conway als Microsoft® Windows® Forms-Anwendung entwickelt. Sie sollten einen Blick auf unseren letzten Artikel werfen, um zu erfahren, was Life ist und wo es herkommt – und weil wir große Teile dieses Codes verwenden.

Dieses Mal werden wir es in eine größtenteils serverseitige Microsoft® ASP.NET-Anwendung konvertieren – aber wir werden sehen, wie Dr. GUIs erster ernsthafter Vorstoß in clientseitiges JScript die Bearbeitung des Spielbretts viel schneller macht, als wenn wir uns auf serverseitigen Code verlassen.

Sehen wir uns also an, wie Dr. GUI diesen serverseitigen Code vom letzten Mal mit einem neuen Webclient vermännte. Die Webanwendung sieht wie folgt aus:

Etwas Altes, ...

Wir konnten einen Großteil des Clientcodes aus der Windows Forms-Anwendung wiederverwenden.

Die größte Wiederverwendung waren die Methoden, die ein Life-Board aus einem anderen berechnen. Wir haben die gesamte Datei LifeCalc.vb aus dem alten Projekt ohne jegliche Änderungen in das neue Projekt kopiert.

Wir haben jedoch eine andere Überladung der CalcNext-Methode aufgerufen. In der Windows Forms Version haben wir die Überladung aufgerufen, die zwei Arrays akzeptiert – das alte array und das neue Array – und die Verweise auf die Arrays anschließend ausgetauscht. Anders ausgedrückt: Wir haben die beiden Arrays erstellt, als wir die Anwendung gestartet haben, und dasselbe Arraypaar verwendet, bis die Anwendung beendet wurde. Der Code zum Berechnen einer neuen Generation und zum Austauschen der Arrays sieht wie folgt aus:

    Dim newBoard As Integer(,) = _
        LifeCalc.CalcNext(currBoard, tempBoard)
    tempBoard = currBoard
    currBoard = newBoard

In der ASP.NET Version ist der Lebenszyklus unserer Anwendung unterschiedlich: Alle variablen werden mit JEDER HTTP-ANFORDERUNG initialisiert. Dies wird als "zustandsloser" Vorgang bezeichnet. (Im Gegensatz zur Windows Forms Version, in der alle Arten von Zustand beibehalten wurden – die beiden Arrays, die Bitmap zum Zeichnen, das Graphics-Objekt usw. – vom Anfang der Anwendung bis zum Ende.)

Wenn wir den Zustand zwischen HTTP-Anforderungen speichern möchten, müssen wir etwas Besonderes mithilfe eines Speichermechanismus ausführen: ein Cookie, ein Eingabefeld (möglicherweise ausgeblendet) im Formular oder, am flexibelsten, sitzungs- oder ansichtszustand. Das bedeutet, dass jede HTTP-Anforderung bewirkt, dass das Array neu erstellt wird – es gibt keine gute Möglichkeit, die Arrays einfach liegen zu lassen. (Dieser zustandslose Vorgang ist typisch für Webanwendungen und verbessert die Skalierbarkeit.)

Da beide Arrays sowieso jedes Mal in der ASP.NET Version erstellt werden müssen, haben wir das aktuelle Board in Page_Load und das neue Board in der Überladung von CalcNext erstellt, die automatisch ein Ergebnisarray für uns erstellt und einen Verweis darauf zurückgibt. Der Code dafür (auch der Handler für die Schaltfläche "Einzelner Schritt") sieht wie folgt aus:

Private Sub RunSingle_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles RunSingle.Click
    currBoard = CalcNext(currBoard)
End Sub

Dadurch wird currBoard so festgelegt, dass es auf das neu erstellte aktualisierte Board verweist, wodurch das alte Array weggeworfen wird (da nichts mehr darauf verweist). Aber wir sind sowieso kurz vor dem Ende, sodass es nichts Falsch ist, das alte Array ein wenig früh wegzuwerfen. (Der Garbage Collector wird schließlich sauber.)

Wir haben auch die FillRandom-Methode unverändert verwendet. Der gute Arzt hat es einfach aus der Windows Forms Anwendung in die ASP.NET Anwendung kopiert! Sie sehen den Code im letzten Artikel.

Etwas Neues, ...

Um alle neuen Dinge zu verstehen, einschließlich clientseitiger Skripts und ASP.NET Ereignishandlern, müssen wir den Lebenszyklus einer Webseite und einer ASP.NET-Anforderung besprechen, damit wir klar sind, wann jedes Codebit ausgeführt wird. (Dr. GUI ist sich bewusst, dass dies für viele von Ihnen eine Überprüfung ist. Dennoch fand er es hilfreich, darüber nachzudenken, wie er dieses Programm schrieb, da er relativ neu in der Webentwicklung ist, also dachte er, dass einige von Ihnen es praktisch finden könnten.)

Lebenszyklus einer Webseite

Was Sie über den Lebenszyklus einer Webseite wissen müssen, ist für diese Anwendung ziemlich einfach: Zuerst gibt der Benutzer die URL für die Seite ein oder klickt darauf. Der Browser lädt die Seite, und er führt jedes Skript in der window_onload-Funktion aus, sofern vorhanden. (Unsere Anwendung benötigt eine, um mit dem Timer umzugehen, da jedes Laden der Webseite alle Variablen neu festlegt und den Timer neu startet, wenn er ausgeführt werden soll.)

Als Nächstes wird die Webseite im Browser des Benutzers angezeigt und wartet auf jede Benutzerinteraktion. Benutzerinteraktionen können in Form von Eingabe- oder Mausereignissen erfolgen. Die meisten Ereignisse verfügen über eine Standardaktion (die Eingabe bewirkt, dass Text im aktuell ausgewählten Textfeld angezeigt wird, und Schaltflächen führen dazu, dass das Formular für instance an den Server übermittelt wird), aber es ist möglich, skript jedem Ereignis zuzuordnen. Diese Anwendung verfügt über ein Skript für Mausklicks auf dem Life Board und für die Schaltfläche zum Aktivieren/Deaktivieren des Zeitgebers sowie über einen Ereignishandler zum Behandeln des Zeitgeberstrichs.

Es ist möglich, durch einen Klick oder einen Aufruf im Skript eine Anforderung an den Server für eine neue Webseite zu generieren. Alle unsere Schaltflächen mit Ausnahme der Schaltfläche zum Aktivieren/Deaktivieren des Timers generieren Serveranforderungen, und der Zeitgeber-Tick-Handler generiert eine, indem die Klickmethode einer der Schaltflächen aufgerufen wird – im Wesentlichen, indem sie programmgesteuert darauf geklickt wird.

Da ein Timer-Tick indirekt eine Serveranforderung generiert, was dazu führt, dass eine neue Webseite generiert und an den Browser zurückgegeben wird, erhalten wir nie mehr als ein Timer-Tick für eine bestimmte instance der Seite.

Wenn wir den Timer dagegen für mehr als einen Tick am Laufen halten möchten, benötigen wir eine Möglichkeit, die Informationen, die der Timer aktivieren soll, an die neue Webseite zu übergeben. Im Folgenden wird erläutert, wie dies funktioniert, wenn wir den Code auf der Webseite besprechen. Im Folgenden wird auch erläutert, wie ASP.NET diese Serveranforderungen behandeln. (Sie können sich vorstellen, den folgenden Abschnitt mit dem Titel "Lebenszyklus einer ASP.NET Request" hier einzufügen, wenn Sie möchten.)

Schließlich wird die Seite entladen– vielleicht klickt der Benutzer auf einen Link oder eine Schaltfläche oder schließt den Browser. Sie können das Entladeereignis bei Bedarf abfangen, aber unsere Anwendung benötigt es nicht.

Wenn der Benutzer danach eine neue Seite angefordert hat, wird die neue Seite geladen (es kann sich um dieselbe Seite mit anderen Formulardaten wie in Life handelt) und der Zyklus beginnt erneut.

Lebenszyklus einer ASP.NET-Anforderung

Sobald die Anforderung an den Server gesendet wird, erfolgt eine Kette von Ereignissen. Für diese Anwendung befassen wir uns nur mit einigen dieser Ereignisse, aber wir befassen uns mit einem ungewöhnlichen Ereignis, das beachtet werden sollte.

Das erste Ereignis, das uns wichtig ist, ist das Seitenladeereignis. Alles, was wir im Seitenladeereignishandler tun, ist, das Array zu initialisieren– entweder aus dem ausgeblendeten Textfeld im Objekt zu lesen oder ein neues Array zu erstellen und es in ein zufälliges Muster zu initialisieren, wenn auf der Webseite kein Array vorhanden ist (für instance, wenn die Seite zum ersten Mal angezeigt wird).

Wenn die Anforderung auf ein Schaltflächenklickereignis zurückzuführen ist, wird als Nächstes die entsprechende Ereignishandlermethode (z. B. RunSingle_Click , wenn auf die Schaltfläche mit einem einzelnen Schritt geklickt wird) ausgeführt. Dadurch kann das Array geändert werden.

Schließlich verwenden wir das Pre-Render-Ereignis der Seite, um den Wert des Arrays in eine Tabelle zu schreiben, die auf der Webseite und in ein ausgeblendetes INPUT-Element gerendert wird, damit der Wert auf Clientseite bearbeitet werden kann.

ASP.NET rendert dann die Webseite aus allen Steuerelementen (einschließlich des HTML-Tabellentexts, der in einem DIV-Element gespeichert ist).

Weitere Informationen zum grundlegenden Lebenszyklus eines Webformulars findest du unter Web Forms Seitenverarbeitung. Weitere Details (einschließlich des Vorabrenderingereignisses) finden Sie unter Control Execution Lifecycle.

Einige Aufgaben auf dem Client, einige auf dem Server

Wie bereits erwähnt, wird ein Teil der Verarbeitung zwischen dem Client und dem Server aufgeteilt. Sehen wir uns den Code in der Reihenfolge an, in der er ausgeführt wird, um zu sehen, wo die einzelnen Verarbeitungsbits ausgeführt werden.

Erstens: Erstellen der Webseite als Reaktion auf die Browseranforderung

Das erste, was bei der Ausführung dieser Anwendung geschieht, ist, dass der Benutzer zur URL für die Anwendung navigiert.

In diesem Fall erstellt ASP.NET ein Seitenobjekt und beginnt mit dem Aufrufen von Methoden, einschließlich der Page_Load-Methode , die der erste auszuführende Code ist. Unsere sieht wie folgt aus:

    Private Sub Page_Load(ByVal sender As System.Object, _
            ByVal e As System.EventArgs) Handles MyBase.Load
        ' don't bother loading array if button clicked was Clear
        If (Request.Form.Get("Clear") = Nothing) Then
            ' wasn't Clear, so get array
            'currBoard = Session("currBoard")
            currBoard = GetBoardFromString(LifeData.Value)
            If currBoard Is Nothing Then
                currBoard = New Integer(rows - 1, cols - 1) {}
                FillRandom(200, False)
            End If
        End If
    End Sub

Hier überprüfen wir, ob die Anforderung erfolgt ist, weil auf die Schaltfläche Löschen geklickt wurde. Wenn dies der Fall ist, löschen wir das Array sowieso, sodass es keinen Sinn hat, es zu erstellen.

Wenn die Anforderung nicht das Ergebnis des Klickens auf die Schaltfläche Löschen ist (und da dies unser erster Besuch auf der Seite ist, ist dies nicht der Fall), versuchen wir, das Array aus dem ausgeblendeten Eingabeelement zu erstellen, indem Wir GetBoardFromString aufrufen. Wenn dies fehlschlägt, erstellen wir ein neues Array und füllen es mit zufälligen Leben. (Dies wird unser erstes Mal passieren, da das ausgeblendete Eingabeelement noch nichts enthält.)

Bei unserem ersten Durchlauf wird keiner der Schaltflächen-Klick-Ereignishandler aufgerufen . Die Seite wurde dem Benutzer noch nicht einmal angezeigt, sodass es keine Möglichkeit gab, auf die Schaltflächen zu klicken!

Das Prärenderingereignis wird jedoch ausgelöst, sodass wir das Array in den HTML-Code rendern können, der eine Tabelle darstellt, die an der richtigen Stelle der Tabelle in das DIV-Element eingefügt werden soll. Wir nutzen diese Gelegenheit auch, um das Array in eine Zeichenfolge zu rendern, die als ausgeblendetes Element an die Seite übergeben werden soll. Im Folgenden finden Sie den Code für die Behandlung des Prärenderingereignisses und für das Rendern in einer Tabelle und einer Zeichenfolge:

    Private Sub Page_PreRender(ByVal sender As Object, _
            ByVal e As System.EventArgs) Handles MyBase.PreRender
        LifeBoard.InnerHtml = _
            RenderLifeToTable(currBoard)
        LifeData.Value = RenderLifeToString(currBoard)
        'Session("currBoard") = currBoard
    End Sub

Dieser Code legt fest, dass die InnerHtml-Eigenschaft des LifeBoard DIV-Elements das HTML-Rendering der Tabelle enthält. (Wir zeigen, wie diese Zeichenfolge in einem Moment erstellt wird.) Anschließend wird das Array auch in eine Zeichenfolge gerendert.

So wird das Array als HTML-Tabelle gerendert:

    Private Function RenderLifeToTable(ByVal board As Integer(,)) _
            As String
        Dim ret As New StringBuilder( _
                        "<table cellspacing=0 cellpadding=0>")
        Dim i, j As Integer
        For i = 0 To rows - 1
            ret.Append("<tr>")
            For j = 0 To cols - 1
                ret.AppendFormat( _
                    "<td><img id = ""IMG_{0}_{1}"" src={2}.gif></td>", _
                    i, j, IIf(currBoard(i, j) = 0, "empty", "alive"))
            Next
            ret.Append("</tr>")
        Next
        ret.Append("</table>")
        Return ret.ToString()
    End Function

Zunächst erstellen wir einen StringBuilder, nicht eine Zeichenfolge, um den resultierenden HTML-Code zu speichern. Wir initialisieren es als öffnendes Tag der Tabelle.

Anschließend wechseln wir in eine Doppelschleife, in der wir TR-Elemente> (Tabellenzeilen) anfügen<, die einen Satz von <TD-Elementen> (Tabellendaten) enthalten. Beachten Sie, dass jede <TD> eine einzelne Zelle darstellt und ein <IMG-Tag> enthält, das angibt, ob die Zelle ein Leben anzeigen soll oder nicht. Beachten Sie außerdem, dass jedes der <IMG-Tags> über eine eindeutige ID verfügt, die die Zeilen- und Spaltennummer enthält. Wir verwenden diese ID, um zu ermitteln, auf welche Zelle später in unserem Microsoft® JScript-Code geklickt wurde. (Die Verwendung dieser IDs erspart uns den Versuch, die Position aus Pixeloffsets zu berechnen. Stattdessen lassen wir den Browser die ID des angeklickten Elements melden und dann die Unterschreibungen aus der ID analysieren.)

Abschließend schließen wir jedes Tag und geben dann den Zeichenfolgen-Generator als Zeichenfolge zurück.

Das Rendern des LifeBoard-Arrays in eine Zeichenfolge, damit wir es in einem ausgeblendeten Eingabeelement speichern können, ist noch einfacher. Es ähnelt dem obigen Code, rendert aber nur "0" und "1" Zeichen, um anzugeben, ob ein Leben vorhanden ist oder nicht:

    Private Function RenderLifeToString(ByVal board As Integer(,)) _
    As String
        Dim ret As New StringBuilder(rows * cols)
        ret.Length = rows * cols
        Dim i, j As Integer
        Dim charCount As Integer = 0
        For i = 0 To rows - 1
            For j = 0 To cols - 1
                ret(charCount) = _
                    IIf(board(i, j) = 0, "0", "1")
                charCount += 1
            Next
        Next
        Return ret.ToString()
    End Function

In diesem Fall weisen wir den StringBuilder der richtigen Länge zu, indem wir den richtigen Konstruktor aufrufen, um seine maximale Länge festzulegen, und dann seine Länge richtig festlegen, sodass der StringBuilder auf die richtige Größe vergrößert wird. Anschließend legen wir jedes Zeichen richtig fest. Dies ist schneller als die Verwendung von Append oder AppendFormat, aber im Tabellenrendering wäre es schwierig, die Zeichenfolge ohne die append-Methoden zu erstellen, da die Größe der Elemente variabel sein kann.

ASP.NET rendert dann die gesamte Seite, einschließlich des DIV und der ausgeblendeten EINGABE, und sendet sie an den Browser.

Zweitens: Was passiert im Browser?

Die Seite wird dann vom Browser geladen und nach dem Ausführen der OnLoad-Funktion angezeigt. Diese Funktion wird im Abschnitt zum Timer (unten) erläutert. Beim ersten Durchlauf wird der Timer nicht festgelegt, sodass die OnLoad-Funktion ohne etwas zurückgibt.

Der Benutzer kann dann mit der Seite interagieren. Wenn der Benutzer auf eine andere Schaltfläche als die Timer-Umschaltschaltfläche klickt, wird eine Anforderung für den Server generiert, und der Zyklus beginnt erneut mit dem Auslösen des Seitenladeereignisses auf dem Server, dem behandelten Ereignis und der Seite, die mit den neuen Werten gerendert wird.

Wenn der Benutzer auf die Umschaltschaltfläche des Timers klickt, wird JScript ausgeführt, um den Timer einzurichten. Dies erfordert drei Funktionen, um richtig zu funktionieren, daher werden wir es separat besprechen.

Die andere Sache, die der Benutzer tun kann, ist, auf den LifeBoard-Tisch selbst zu klicken. Der Klickhandler zum Bearbeiten des LifeBoards folgt:

function LifeBoard_onclick() { 
imgID = event.srcElement.id; 
sa = imgID.split("_"); // format is "IMG__" 
// sa[0] should contain "IMG" 
if (sa[0] == "IMG") { 
row = sa[1]; col = sa[2]; 
// 32 is the number of columns--be sure to change if 
// you change it on the server side!!!! 
linearPos = row * 32 + new Number(col); 
with (document.Form1) { 
firstPart = LifeData.value.substr(0, linearPos); 
lastPart = LifeData.value.substr(linearPos + 1); 
if (LifeData.value.charAt(linearPos) == "0") { 
middlePart = "1"; 
document.getElementById(imgID).src = "alive.gif"; } 
else { 
middlePart = "0"; 
document.getElementById(imgID).src = "empty.gif"; 
} 
LifeData.value = firstPart + middlePart + lastPart; 
} 
} 
}

Dieser clientseitige Code sucht zuerst die ID des Bilds, auf das geklickt wurde, und teilt diese ID dann in ein Array von Unterzeichenfolgen auf. Anschließend wird sichergestellt, dass die ID mit "IMG" beginnt und die URL des entsprechenden IMG-Tags ändert, damit sie ordnungsgemäß angezeigt wird. Außerdem ändern wir den Inhalt des ausgeblendeten Eingabeelements an der richtigen Stelle, damit wir die Daten ordnungsgemäß an die Serverseite übergeben können. Beachten Sie, dass diese Funktion KEINE serverseitige Verarbeitung generiert – die gesamte Verarbeitung erfolgt hier auf dem Client!

JScript hat keine Möglichkeit, ein einzelnes Zeichen in der Mitte einer Zeichenfolge zu ändern. Daher müssen wir eine neue Zeichenfolge erstellen, indem wir die alte Zeichenfolge bis kurz vor dem geänderten Zeichen, dem geänderten Zeichen und dem Rest der alten Zeichenfolge verketten. Mit anderen Worten, wenn wir das mittlere Zeichen ("X") in "000X111" in ein "Y" ändern möchten, würden wir die unveränderten ersten drei Zeichen , "000", das geänderte Zeichen "Y" und die unveränderten letzten drei Zeichen, "111", verketten.

Funktionsweise des Timers

Der Timer erfolgt vollständig auf der Clientseite (mit ausnahme natürlich der Anforderung an den Server, ein neues Board zu berechnen).

Die Tatsache, dass wir eine Anforderung an den Server stellen, bedeutet, dass alle JScript-Variablen gelöscht werden, wenn die Seite zurückkommt. Wenn wir also den Status des Timers beibehalten möchten, müssen wir dies auf eine Weise tun, die von Anforderung zu Anforderung dauerhaft ist.

Dazu wählen wir ein ausgeblendetes Eingabeelement aus. Hier ist der Code, der das ausgeblendete Eingabeelement (und damit den Zustand des Timers) umschaltt:

function ToggleAuto_onclick() {
   with (document.Form1) {
      if (TimerEnabled.value != "true") { // timer not running
         // hidden form value for postback
         TimerEnabled.value = "true";
         // change button right away in case server slow
         ToggleAuto.value = "Stop!!!";
         ToggleAuto.style.backgroundColor = "red";
         // run 1st generation, rest handled by onload
         RunSingle.click();
      }
      else {
         // don't change if already refreshing
         if (!TickHappening) {
            // shut off timer, clear reference 
            clearTimeout(TimerID);
            // hidden form value for postback
            TimerEnabled.value = "false";
            // change button in UI
            ToggleAuto.value = "Start repeating";
            ToggleAuto.style.backgroundColor = "Lime";
         }
      }
   }
}

Wenn der Timer hier nicht ausgeführt wird (der Anfangszustand), ändern wir das ausgeblendete Eingabeelement so, dass es "true" enthält, und dann ändern wir die Schaltfläche in einen Stoppzustand und führen die erste neue Generation aus. Wenn wir zurückkommen, legt die OnLoad-Funktion (unten) den Timer so fest, dass die Generierung danach berechnet wird. Hier ist OnLoad:

function window_onload() {
   with (document.Form1) {
      // page loading, timer off; should we set?
      if (TimerEnabled.value == "true") {
         msec = RepSecs.value * 1000;
         if (msec < 2000) { // not too fast!
            msec = 2000;
            RepSecs.value = "2";
         }
         // have to override default page values
         ToggleAuto.value = "Stop!!!";
         ToggleAuto.style.backgroundColor = "red";
         // set the timer...note only one interval
         TimerID = setTimeout(TickHandler, msec);
      }
   }
}

Wenn der Timer aktiviert ist (gemäß dem ausgeblendeten Eingabeelement), berechnen wir die Anzahl von Millisekunden basierend auf dem Wert eines INPUT-Elements , um den Timer festzulegen (mindestens 2000 msec, oder mindestens zwei Sekunden), ändern Sie die Farbe/den Text der Schaltfläche (falls nicht bereits geändert), und legen Sie dann einen Countdown-Timer fest.

Wenn der Timer ausgelöst wird, wird die Tick-Ereignishandlermethode aufgerufen:

function TickHandler() {
   with (document.Form1) {
      if (TimerEnabled.value == "true") { // eliminate stray ticks
         TickHappening = true;
         RunSingle.click();
      }
   }
}

Diese Funktion klickt hauptsächlich auf die Einzelschrittschaltfläche, stellt jedoch sicher, dass der Timer weiterhin aktiviert ist. Ohne diese Überprüfung ist es möglich, dass ein Tick direkt nach dem Ausschalten des Timers kommt. Wir legen auch eine Variable mit dem Namen TickHappening fest, damit sie in der Schaltflächen-Umschaltmethode überprüft werden kann– wiederum, um Racebedingungen zu verhindern.

Alle anderen Funktionen werden auf dem Server verarbeitet, und der größte Teil dieses Codes ist sehr ähnlich, aber nicht identisch mit dem Windows Forms Anwendung vom letzten Mal.

Etwas geliehen, und...

Sehen wir uns also an, was passiert, wenn wir auf eine der serverseitigen Ereignisschaltflächen klicken. Zunächst wird der oben gezeigte Page_Load-Ereignishandler aufgerufen. Dieses Mal muss ein Array abgerufen werden . Daher erstellen wir ein Array aus der Zeichenfolge, die im ausgeblendeten Eingabeelement gespeichert ist. Der Code für die Analyse, der sich hier befindet:

    Private Function GetBoardFromString(ByVal s As String) As Integer(,)
        If (s.Length <> rows * cols) Then ' string not present
            Return Nothing
        Else
            Dim board = New Integer(rows - 1, cols - 1) {}
            Dim i, j As Integer
            For i = 0 To rows - 1
                Dim rowStart As Integer = i * cols
                For j = 0 To cols - 1
                    board(i, j) = _
                        IIf(s.Chars(rowStart + j) = "0", 0, 1)
                Next
            Next
            Return board
        End If
    End Function

Wenn die Zeichenfolge nicht vorhanden ist oder die falsche Länge hat, geben wir einfach Nothing als Fehler zurück. Andernfalls erstellen wir ein Array und analysieren dann die Zeichenfolge, um die Elemente des Arrays festzulegen.

Wenn als Nächstes auf eine Schaltfläche geklickt wurde, wird einer der folgenden Ereignishandler aufgerufen:

    Private Sub AddRandom_Click(ByVal sender As System.Object, _
            ByVal e As System.EventArgs) Handles AddRandom.Click
        FillRandom(NumRnd.Text, False)
    End Sub

    Private Sub Clear_Click(ByVal sender As System.Object, _
            ByVal e As System.EventArgs) Handles Clear.Click
        ' array not created in OnLoad; must create now!
        currBoard = New Integer(rows - 1, cols - 1) {}
    End Sub

    Private Sub RunSingle_Click(ByVal sender As System.Object, _
            ByVal e As System.EventArgs) Handles RunSingle.Click
        currBoard = CalcNext(currBoard)
    End Sub

    Private Sub RunMult_Click(ByVal sender As System.Object, _
            ByVal e As System.EventArgs) Handles RunMult.Click
        Dim num As Integer = NumOfGen.Text
        Dim i As Integer
        For i = 1 To num
            currBoard = CalcNext(currBoard)
        Next
    End Sub

Diese Handler tun so ziemlich das, was sie beim letzten Mal getan haben, aber da die Seite automatisch gerendert wird, müssen sie keine Routine aufrufen, um zu rendern und zu aktualisieren.

Außerdem funktioniert der clear-Handler anders: Anstatt ein Array im Ladehandler zu erstellen und es dann zu löschen, überspringen wir einfach das Erstellen des Arrays im Ladehandler (erinnern Sie sich an den If-Anweisungstext), und erstellen Sie das Array im clear-Handler neu.

Schließlich ist der Handler für mehrere Generationen neu. Es ruft einfach CalcNext in einer Schleife auf.

Etwas Blau!

Nun, es gibt nicht wirklich viel Blau hier, aber um eine gute Hochzeit zwischen ASP.NET und Life zu haben, könnten wir die Tatsache in Betracht ziehen, dass es kein HTML-basiertes Schiebereglersteuerelement gibt, um eine traurige Sache zu sein, und das könnte uns blau machen.

Entwurfsoptionen

Jede Anwendung umfasst einige Optionen zwischen verschiedenen Designs. Die offensichtlichste in dieser Anwendung war die Entscheidung, welche Dinge auf dem Server ausgeführt werden sollen und welche Dinge auf dem Client ausgeführt werden sollen.

Es ist klar, dass Sie die gesamte Verarbeitung auf dem Client durchführen können, einschließlich der Berechnung von Generationen. JScript ist ziemlich langsam, sodass der gute Arzt spekuliert, dass die Lebensdauerberechnung in JScript möglicherweise langsamer ist als auf dem Server. (Dr. GUI gibt jedoch zu, dass er dies nicht getestet hat, und auf einem wirklich schnellen Desktop ist es möglicherweise schneller, diese Clientseite zu tun, sogar mit Skript.)

Auf der anderen Seite könnte alles auf der Serverseite sein, einschließlich der Bearbeitung des Boards. Das Bearbeiten der Boardserverseite wäre jedoch schmerzhaft langsam, da für jede Bearbeitungsänderung ein Roundtrip zum Server erforderlich wäre. Autsch!

Sie könnten wirklich darüber streiten, ob das clear-Ereignis auf dem Server oder Client erfolgen soll. Es wäre einfach genug, dies auf dem Client zu tun, aber wir hatten bereits den serverseitigen Code ausgeführt, und die Benutzer erwarten nicht, dass clear sehr schnell ist, also hat der gute Arzt einfach gut genug alleine gelassen. (Wenn es nicht kaputt ist, beheben Sie es nicht!)

Versuch es doch mal!

Wenn Sie .NET haben, können Sie es lernen, es auszuprobieren ... und falls nicht, erwägen Sie es bitte. Wenn Sie eine Stunde oder so eine Woche mit Dr. GUI .NET verbringen, sind Sie in kürzester Zeit ein Experte im .NET Framework!

Seien Sie der Erste in Ihrem Block – und laden Sie einige Freunde ein!

Es ist immer gut, der Erste zu sein, der eine neue Technologie lernt, aber noch mehr Spaß macht es mit einigen Freunden! Organisieren Sie für mehr Spaß eine Gruppe von Freunden, um .NET zusammen zu lernen!

Einige Dinge, die Sie ausprobieren können...

  • Probieren Sie zunächst den hier gezeigten Code aus. Spielen Sie mit dem Code einige.
  • Fügen Sie einige verschiedene Schaltflächen hinzu, um lustige Dinge zu erledigen. Vielleicht haben Sie ein Menü mit Bestandsmustern, die Sie der Tafel hinzufügen können.
  • Probieren Sie dies mit verschiedenen Datenstrukturen aus, und sehen Sie sich an, wie es funktioniert.

Was wir getan haben; Was kommt als nächstes

Dieses Mal haben wir Das Game of Life von Conway als ASP.NET-Anwendung gemacht – mit einigen strategischen clientseitigen JScript-Methoden. Beim nächsten Mal zeigen wir, wie Formatierung und Analyse im .NET Framework funktionieren.