Anmelden
Ich möchte für die nächsten 30 Tagen angemeldet bleiben
Deutsch
Several pages in the usergroup are available in English. Click on english to visit these pages.

DotNetNuke News

26.04.2008
DNN-Modulprogrammierung - ein einfaches Beispiel (Schritt 4b - Die Präsentationsschicht - Das View-Control in Visual Basic .Net) (Michael Tobisch)
Nachdem wir nun doch einigen Code geschrieben haben ohne etwas zu "sehen" geht es nun endlich zur Sache: Die Präsentationsschicht stellt die Schnittstelle unserer Applikation zum Benutzer dar, und das ist nun unser eigentliches Modul.

Zunächst einmal legen wir im Ordner DesktopModules unserer DotNetNuke-Installation einen neuen Ordner an, der gleich heißt wie der Ordner im App_Code, nämlich DnnUgDe_Contacts. (Ich habe ihn - um nicht mit der C#-Variante über Kreuz zu kommen - DnnUgDe_ContactsVB genannt, also hier nicht verwirren lassen. Wenn jemand beide Varianten nachprogrammiert, dann sollte er das VB immer dazusetzen, insbesondere bei der Moduldefinition. Ich verwende hier teilweise Screenshots aus dem C#-Tutorial, also bitte darauf achten!). Hierher kommen nun alle weiteren Steuerelemente, die wir für unser Projekt benötigen. Es sind dies:

  • ein Ansichts-Steuerelement
  • ein Bearbeitungs-Steuerelement

Das View-Control - Visual Basic .Net

Beginnen wir mit unserem Ansichtselement: Legen wir im soeben erzeugten Ordner ein neues Element an:

ContactsView.ascx

Wählen wir Web-Benutzersteuerelement, stellen wir als Sprache Visual Basic ein, geben den Name ContactsView.ascx an und achten wir darauf, dass das Kontrollkästchen Code in eigener Datei platzieren aktiviert ist. Dann klicken wir auf Hinzufügen.

Zunächst einmal öffnen wir auch unsere Code-Datei (ContactsView.ascx.cs - diese verbirgt sich eventuell hinter dem +-Zeichen links neben der soeben angelegten Datei):

Code-Datei öffnen

Im Unterschied zu den in den vergangenen Tutorials sehen wir hier eine "partielle" Klasse. Was bedeutet das? Kurz gesagt kann man im .Net-Framework (seit Version 2.0) Klassen auf mehrere einzelne Klassendefinitionen aufteilen. Dabei können die partiellen Klassendefinitionen auch auf mehrere Dateien aufgeteilt sein. Partielle Klassen werden in Web-Forms und Windows-Forms verwendet, können aber natürlich auch angewendet werden, wenn mehrere Entwickler an einer Klasse arbeiten, oder Teile einer Klasse manuell codiert, andere Teile aber automatisch generiert werden, z.B. durch ein CASE-Tool oder aber wenn man in der Anwendung sogenannte Typed Datasets verwendet. Durch dieses Konzept lassen sich automatisch generierte (partielle) Klassen erweitern, ohne dass man Code verliert, wenn man die Klasse durch den Generator neu erstellen lässt. Ein schönes Beispiel findet man in Scott Mitchells Data Access Tutorials im Abschnitt "Adding Custom Code to the DAL".

Packen wir unsere partielle Klasse zunächst wieder in einen Namespace, und leiten wir sie nicht wie vorgegeben von der Klasse UserControl, sondern von der Klasse PortalModuleBase aus dem Namespace DotNetNuke.Entities.Modules ab. Nennen wir die partielle Klasse ContactsView. Unser Code sieht nun wie folgt aus:

Namespace DnnUgDe.DNN.Modules.VB.Contacts
   Partial Class ContactsView
      Inherits PortalModuleBase

   End Class
End Namespace

Im Benutzersteuerelement selbst (ContactsView.ascx) müssen wir nun noch das Inherits-Attribut unseren Änderungen anpassen. Öffnen wir die Datei in der Quellcode-Ansicht, und weisem dem Attribut den Wert "DnnUgDe.DNN.Modules.VB.Contacts.ContactsView" zu:

<%@ Control Language="VB" AutoEventWireup="false" CodeFile="ContactsView.ascx.vb"
   Inherits="DnnUgDe.DNN.Modules.VB.Contacts.ContactsView" %>

Wechseln wir nun in die Entwurfs-Ansicht. Ziehen wir aus der Toolbox ein GridView-Steuerelement auf den Entwurf. Mit Hilfe des GridViews werden wir die Daten tabellarisch darstellen. Im Eigenschaften-Fenster Geben wir dann dem Steuerelement die ID ContactsGrid.

Eigenschaften - ContactsGrid

Öffnen wir nun das Smart-Tag des Grids und wählen wir aus der Liste Datenquelle auswählen die Option Neue Datenquelle…:

Smart-Tag - Neue Datenquelle

Im Assistenten, der nun erscheint wählen wir einmal Objekt als Datenquellentyp aus (die notwendigen Datenobjekte haben wir ja im letzten Schritt erstellt), und nennen die Datenquelle ContactsDataSource:

Datenquelle konfigurieren - Schritt 1

Klicken wir nun auf OK. Im nächsten Schritt wählen wir aus der DropDownListe unser gewünschtes Geschäftsobjekt aus:

Datenobjekt auswählen

Um die angezeigte Liste radikal zu verkürzen, können wir das Kontrollkästchen "Nur Datenkomponenten anzeigen" auswählen - und so finden wir DnnUgDe.DNN.Modules.VB.Business.ContactController recht einfach:

Datenobjekt auswählen - nur Datenkomponenten anzeigen

Erinnern wir uns: in Schritt 3 haben wir den ContactController als <DataObject()> gekennzeichnet, und die Methoden mit den entsprechenden Attributen belegt. Hätten wir das nicht getan, müssten wir den Controller jetzt aus der langen Liste auswählen - er wäre nicht als Datenobjekt gekennzeichnet. In den folgenden Schritten sehen wir, dass sich auch die (kurze) Arbeit mit den Attributen für die Methoden ausgezahlt hat: Im Register SELECT sehen wir nur Auswahlmethoden, im Register INSERT nur Einfügemethoden etc.

Klicken wir nun auf Weiter. Im nächsten Bild müssen wir die Methode angeben, mit denen die Datensätze ausgewählt werden. Nachdem wir hier alle Datensätze anzeigen wollen, wählen wir GetAll:

SELECT-Methode wählen

Hier ist keine Methode vorausgewählt. Das liegt daran, dass wir bei allen Select-Methoden unseres Controllers im Attribut False angegeben haben:

...
      <DataObjectMethod(DataObjectMethodType.Select, False)> _
      Public Function GetAll(ByVal ModuleID As Integer) As List(Of ContactInfo)
...
      <DataObjectMethod(DataObjectMethodType.Select, False)> _
      Public Function GetContact(ByVal ContactID As Integer) As ContactInfo
...

Hätten wir hier bei einer Methode True angegeben, so wäre diese als Standard-Select-Methode gekennzeichnet und vorausgewählt.

Nachdem wir die Methode GetAll ausgewählt haben, klicken wir auf Weiter. Wir wollen dem Grid keine Bearbeitungsmöglichkeiten geben, dies ist der "reine" Ansichtsteil unseres Moduls. Nachdem wir auch keine Insert-, Update- oder Delete-Methode als Standard gekennzeichnet haben, können wir die entsprechenden Register aussen vor lassen und klicken auf Weiter.

Im letzten Schritt des Assistenten können wir die für die Abfrage notwendigen Parameter festlegen. Der Parameter ModuleID steht aber erst zur Laufzeit fest, und so müssen wir ihn im Code zuweisen:

Parameter

Wir klicken auf Fertig stellen und beenden den Assistenten.

Eine Kleinigkeit im Markup sollten wir noch ändern, bevor wir weitermachen. Visual Studio setzt beim Erstellen einer Objektdatenquelle automatisch das Attribut OldValuesParameterFormatString mit dem Wert original_{0}. Dieses Attribut wird benötigt, wenn man für Aktualisierungs- und Löschabfragen sogenannte optimistische Konkurrenz verwendet. Ich werde auf diese Variante in einem späteren Beitrag vielleicht einmal eingehen, hier benötigen wir sie nicht - und wir haben darauf auch beim Erstellen unserer Gespeicherten Prozeduren keine Rücksicht genommen. Lässt man dieses Attribut mit diesem Wert stehen, so laufen wir beim Einfügen oder Löschen (was hier ja nicht vorgesehen ist) in eine Ausnahme. Um diesen Fehler zu vermeiden können wir es entweder komplett aus dem Markup löschen, oder den Wert des Attributs auf {0} ändern. Ich verwende hier die erste Variante:

<asp:ObjectDataSource ID="ContactsDataSource" runat="server"
   TypeName="DnnUgDe.DNN.Modules.CS.Contacts.Business.ContactController"
   SelectMethod="GetContacts">
   <SelectParameters>
      <asp:Parameter Name="moduleID" Type="Int32" />
   </SelectParameters>
</asp:ObjectDataSource>

In unserem Fall ist das gar nicht notwendig, weil wir für diese Datenquelle gar kein Einfügen oder Löschen vorgesehen haben - schaden tut es nicht, wenn man sich diesen Schritt von Anfang an angewöhnt.

Um den eben angesprochenen Parameter zuzuweisen genügt eine einzige Zeile im Code (in der Datei ContactView.ascx.vb), und zwar in der Methode Page_Load:

      Private Sub Page_Load(ByVal sender As Object, ByVal e As EventArgs) Handles MyBase.Load
         Me.ContactsDataSource.SelectParameters("ModuleID").DefaultValue = MyBase.ModuleId.ToString();
      End Sub

Schließen wir das Grid vorläufig ab, indem wir noch eine Meldung anzeigen, wenn keine Datensätze vorhanden sind. Nachdem ein "ordentliches" DotNetNuke-Modul lokalisierbar ist, machen wir das gleich mehrsprachig. Legen wir unter unserem Ordner also einen ASP.Net-Ordner App_LocalResources an:

App_LocalResources

Klicken wir mit der rechten Maustaste auf diesen Ordner und wählen wir Neues Element hinzufügen. Wählen wir Resourcendatei, und geben dieser den Namen ContactsView.ascx.resx (die Datei muss den gleichen Namen haben, wie unsere Steuerelementsdatei (inkl.Erweiterung), und mit der Erweiterung resx versehen werden). Das ist unsere Fallback-Datei (wenn für die aktuelle Sprache keine Lokalisierung vorhanden ist), also üblicherweise Englisch.

Resourcendatei

Der Resource-Editor stellt drei Spalten einer Tabelle dar: Name, Wert und Kommentar. Wie diese Resourcen genau funktionieren, kann man an geeigneter Stelle nachlesen, wir legen jetzte einmal eine Zeile an, in der wir folgende Werte eintragen:

Name Wert Kommentar
ContactsGrid.EmptyDataText No contacts available. Message when the underlying table is empty.

(Der Kommentar ist natürlich ohne jede Funktion für das Programm, und kann auch weggelassen werden.)

Fügen wir nun noch eine zweite Zeile in unserem Code ein, die diese Resource ausliest und zuordnet:

   this.ContactsGrid.EmptyDataText = Localization.GetString("ContactsGrid.EmptyDataText", LocalResourceFile);

Dazu benötigen wir noch einen Verweis auf den Namespace DotNetNuke.Services.Localization, und das war's.

Um auch eine deutsche Resourcendatei zu erhalten, müssen wir im selben Ordner eine Datei anlegen, die dann entsprechende ContactsView.ascx.de-AT.resx (für Österreich), ContactsView.ascx.de-CH.resx (für die Schweiz) oder ContactsView.ascx.de-DE.resx (für Deutschland) heißt (oder alle drei), und dort den gleichen Namen mit dem übersetzen Schlüssel einfügen:

Name Wert Kommentar
ContactsGrid.EmptyDataText Keine Kontakte verfügbar. Nachricht, wenn die zugrundeliegende Tabelle leer ist.

Nun können wir unser Modul in die Moduldefinitionen unserer Installation aufnehmen - schließlich wollen wir endlich etwas sehen! Dazu melden wir uns zunächst einmal auf dem Portal in unserer Testinstallation als Host an und wählen im Menü System die Option Module:

System :: Module

Im Modulmenü (oder bei den Aktionslinks am unteren Ende des Contailers) wählen wir Neue Moduldefinition hinzufügen:

Neue Moduldefinition hinzufügen

Im angezeigten Formular müssen wir nun einige Felder ausfüllen:

  • Unter Modulname, Verzeichnisname und angezeigter Name setzen wir den Wert DnnUgDe_Contacts
  • Unter Beschreibung geben wir eine kurze Beschreibung unseres Moduls an, z.B. The contacts module in VB
  • Unter Version lassen wir den vorgegebenen Wert 01.00.00 stehen.
  • Schließlich geben wir unter Steuerklasse noch den vollständigen Namen unseres Controllers an, also DnnUgDe.DNN.Modules.VB.Contacts.Business.ContactController
Moduldefinition erstellen

Klicken wir auf erstellen, um den Vorgang abzuschließen. Nachdem wir den Modul erstellt haben gelangen wir in die Maske Moduldefinition bearbeiten. Wir müssen nun eine Definition hinzufügen. Geben wir dazu im Feld Neue Definition ganz unten den Wert DnnUgDe_Contacts ein und klicken auf Definition hinzufügen:

Definition hinzufügen

Zuguterletzt müssen wir noch unser eben erstelltes Steuerelement dem Modul zuordnen. Dazu klicken wir ganz unten im Formular auf Steuerelement hinzufügen. Im folgenden Formular Steuerelemente des Moduls bearbeiten geben wir unter Titel den Wert View Contacts ein, als Quelle wählen wir DesktopModules/DnnUgDe_Contacts/ContactsView.ascx aus der Liste und alle anderen Felder lassen wir unverändert (wichtig ist, dass wir hier keinen Schlüssel angeben, da dieses ja unser "zentrales" Steuerelement ist, das beim ersten Aufruf des Moduls angezeigt wird):

Steuerelemente des Moduls bearbeiten

Klicken wir zum Abschluss dieses Vorgangs auf speichern. Wir gelangen wieder in das Formular Moduldefinition bearbeiten und klicken hier auf speichern, um die Moduldefinition fertigzustellen.

Moduldefinition speichern

Klicken wir danach auf abbrechen oder wählen wir im Menü System wieder Module oder klicken wir im Breadcrumb auf Module, so sehen wir unser Modul in der Liste der installierten Module:

Moduldefinitionen

Um unser Modul nun einzusetzen, legen wir eine neue (leere) Seite in unserem Portal an (ich nehme nicht an, dass ich hier erklären muss, wie das geht). In der Modulliste des Steuerungsbereichs wird unser Modul nun angezeigt. Wählen wir es aus und fügen es auf unserer Seite ein:

Modul einfügen

Und wie kaum anders zu erwarten wird uns angezeigt, dass in der Datenbank nichts gefunden wurde:

No data

(bzw., wenn wir die deutsche Resource erstellt haben und unser Portal bzw. unsere Sitzung auf die entsprechende deutsche Lokalisierung eingestellt wurde:)

Keine Daten

VB.Net-Quellcode herunterladen

Kommentare: 2

test user meint

Hallo Michael,

gute Arbeit. Weiter so!
Wann geht es weiter?
# 21.05.2008 13:59

Michael Tobisch meint

Hallo test user!

Leider hatte ich in letzter Zeit etwas Stress mit meiner DNN Installation, so dass ich nicht in der Lage war, an dem Ding weiterzuarbeiten. Dazu kam noch einiges anderes, aber ich will euch hier nicht mit irgendwelchen "Ausreden" langweilen. Bald geht's weiiter...
# 22.05.2008 08:00