Autoren: Carsten Schorn und Pascal Brause

Nachdem Entwickler ihr Projekt geschrieben haben, wünschen sich viele eine Bewertung oder Verbesserungsvorschläge ihres Codes herbei. Dafür gibt es bereits zahlreiche, automatisch generierte Analyestools und Metriken. Visual Studio bietet auch eine solche Form von Codeanalyse an, welche sehr umfangreich ist. Die Hinweise sind hier zahlreich: Sie reichen von der Sicherheit über Namenkonventionen und Design bis hin zur Performance. Doch wie kann ich das selbst bestimmen?

Vorhandene Regeln und Regelsätze

Unter den Projekteigenschaften findet sich der Tab „Codeanalyse“. Dort können wir einen von zahlreichen, von Microsoft zu Verfügung gestellten Regelsätzen, auswählen. Ein Regelsatz ist dabei eine Zusammenstellung von Regeln. Wird nun einer der Regelsätze geöffnet, offenbart sich bereits das gesamte Sortiment von vorhandener Regeln.

Bild 1: Vorhandene Regeln von VS 2010

Nun können wir die uns zusagenden Regeln durch Selektieren zusammenstellen. Möchten wir nun diese Regeln auf unser Projekt anwenden, können wir einfach durch einen Rechtsklick im Projektmappenexplorer auf unser Projekt „Codeanalyse“ auswählen. Schon prüft VS 2010 unser Projekt auf alle von uns ausgewählten Regeln des gegebenen Regelsatzes.

Eigene Regelsätze erstellen

Haben wir unsere Lieblingsregeln gefunden und wollen diese dauerhaft abspeichern, ist dies relativ schnell erledigt. Hierzu die gewünschten Regeln auswählen und anschließend unter „Datei“ „speichern unter“ auswählen. Am besten wird diese Datei (*.ruleset) unter dem angebotenen Pfad gespeichert (C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\Rule Sets bei 64-bit Systemen und C:\Program Files\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\Rule Sets bei 32-bit Systemen). Wenn wir nun den Namen und die Beschreibung noch ein wenig aufhübschen wollen, können wir die eben abgespeicherte Datei mit einem Texteditor öffnen, editieren und die ersten beiden Zeilen mit folgendem Codefragment ersetzen.

<?xml version="1.0" encoding="utf-8"?>
<RuleSet Name="Regelname" Description="Beschreibung der Regel." ToolsVersion="10.0">

Nun wird auch in VS 2010 der korrekte Name mit passender Beschreibung angezeigt.

Eigene Regel schreiben

Doch was ist, wenn uns die von Microsoft gegebenen Regeln nicht ausreichen, sondern wir unsere eigene, spezielle Regel aufstellen wollen? Hier wird es ein wenig kniffliger, da Microsoft kaum bis gar keine Dokumentation dazu ausgibt. Doch erstmal eine kurze Erklärung, wie das ganze Konstrukt „eigene Regel“ am einfachsten und effizientesten funktioniert: Wir erstellen ein neues Projekt, schreiben dort die Implementierung der Regel. In eine dazugehörige XML-Datei werden Metadaten hinterlegt, welche mit dem Projekt verknüpft sind. Anschließend bekommen wir eine *.dll-Datei erzeugt, welche unsere Regel darstellt. Zuletzt binden wir diese Regel noch in unseren Regelsatz ein und fertig! Aber alles noch mal Schritt für Schritt:

  1. Zuerst müssen wir eine neue Projektmappe erstellen und ein dazugehöriges Klassenbibliotheks-Projekt (welches uns die *.dll-Datei liefert) hinzufügen. Dieses haben wir in unserem Beispiel „MyCustomFxCopRules“ genannt (gleiche Namen von Projektmappe und Projekt sind hier wichtig!).
  2. Nun müssen wir dem Projekt zwei Referenzen einverleiben, ohne welche unsere Regeln nicht funktionieren würden. Diese sind die FxCopSdk.dll und die Microsoft.Cci.dll. Dazu entladen wir unser Projekt (Rechtklick auf das Projekt, „Projekt entladen“) und öffnen die *.csproj- Datei (Rechtsklick auf das entladene Projekt).
  3. Wir fügen dann folgenden Code in die <ItemGroup> ein:
<Reference Include="FxCopSdk">
  <HintPath>$(CodeAnalysisPath)\FxCopSdk.dll</HintPath>
  <Private>False</Private>
</Reference>
<Reference Include="Microsoft.Cci">
  <HintPath>$(CodeAnalysisPath)\Microsoft.Cci.dll</HintPath>
  <Private>False</Private>
</Reference>
  1. Nachdem wir gespeichert haben, laden wir das Projekt wieder (Rechtsklick auf das Projekt, “Projekt erneut laden“).
  2. Jetzt müssen wir die erwähnte XML-Datei anlegen und mit Inhalt füllen. Dazu im Projektmappenexplorer eine XML-Datei hinzufügen (Rechtklick auf das Projekt, „Hinzufügen“, „Neues Element“, „XML-Datei“ auswählen). Diese haben wir hier „RuleMetadata.xml“ genannt.
  3. In diese XML-Datei stecken wir den unten stehenden Code. Resolution entspricht hier dem Text, der im Fehlerliste-Fenster von VS 2010 angezeigt wird. MessageLevel besagt, was ausgeworfen werden soll, wenn die Regel eintritt. Ebenso FixCategories.
<?xml version="1.0" encoding="utf-8" ?>
<Rules FriendlyName="My Custom FxCop Rules">
  <Rule TypeName="FalscheNotation" Category="MyRules" CheckId="CR1000">
    <Name>FalscheNotation</Name>
    <Description>Prüft, ob alle Felder einer bestimmten Schreibweise entprechen.</Description>
    <Resolution>Feld {0} ist nicht in gewuenschter Notation. Feld Name sollte beginnen mit: '{1}'.</Resolution>
    <MessageLevel Certainty="100">Warning</MessageLevel>
    <FixCategories>NonBreaking</FixCategories>
    <Url />
    <Owner />
    <Email />
  </Rule>
</Rules>

Extrem wichtig ist, dass der TypeName genau der Klasse entspricht, wie wir sie in der Implementation benutzen. Das sehen wir jetzt, denn nun widmen wir uns der Implementierung: Diese eigene geschriebene Regel prüft lediglich, ob der Name eines static-Felds eine bestimmte Schreibweise hat bzw. ob der Name eines nicht-static-Felds eine bestimmte Schreibweise hat. In diesem konkreten Fall wird geprüft ob der Name eines statischen Feldes mit „static_“ beginnt. Sollte dies nicht der Fall sein, wird eine Warnung ausgegeben. Zweck des ganzen sollte vielmehr die Einbindung der Regel in Visual Studio und die Funktionsweise des ganzes sein.

  1. In eine *.cs-Datei (Name in unserem Beispiel :„Definition.cs“, aber prinzipiell egal) fügen wir nun folgenden Code ein:
//Folgende Usings benötigt unser Code:
 using System;
 using System.Collections;
 using System.Collections.Generic;
 using System.Linq;
 using System.Text;
 using System.Xml;
 using Microsoft.FxCop.Sdk;
 //Muss immer im selben Namespace des Projektes geschrieben werden
 namespace MyCustomFxCopRules
 {
   // Die Standart-Klasse erbt von BaseIntrospectionRule, einer von FxCop vorgegeben Klasse
  internal abstract class BaseFxCopRule : BaseIntrospectionRule
  {
    //Deklaration der Rule (BaseFxCopRule), welche als Namen ("MyCustomFxCopRules.RuleMetadata") unbedingt den Projektnamen + Namen der XML Datei beinhalten muss
    protected BaseFxCopRule(string ruleName) : base(ruleName, "MyCustomFxCopRules.RuleMetadata", typeof(BaseFxCopRule).Assembly)
    { }
  }
  //Anschließend Kommt die "eigentliche" Klasse bzw. Rule, welche von der Standard-Klasse wiederrum erbt. Exakte Namenskonvention ist einzuhalten!
 internal sealed class FalscheNotation : BaseFxCopRule
 {
   public FalscheNotation()
     : base("FalscheNotation")
   { }
   //Hier müssen wir überschreiben, dass nicht-externe Code Elemente sichtbar gemacht werden, da es diese sind, die analysiert werden.
   public override TargetVisibilities TargetVisibility
   {
     get
     {
       return TargetVisibilities.NotExternallyVisible;
     }
   }
   //Überschreiben der Methode Check (von FxCop Basismethode zum Überprüfung allermöglichen Dinge). Hier geschieht die eigentliche Logik der Rule.
   //Der Returnwert ist vom Typ ProblemCollection, welcher signalisiert, ob ein Warning/Break/Error o.ä. ausgeworfen werden soll
   //Default wert ist null, was einem "alles in Ordnung" entspricht.
   public override ProblemCollection Check(Member member)
   {
     Field field = member as Field;
     if (field == null)
     {
       //Die Rule erscheint nun nur bei Field
       //Sollte nichts gefunden worden sein, wird ein null als ProblemCollection zurückgegeben, was ein "alles in Ordnung" repräsentiert.
      return null;
     }
     //Unterscheidung, ob das Feld, was gefunden wurde, statisch ist oder nicht....
     if (field.IsStatic)
     {
       //Nun wird geschaut ob ein "static_" vor dem Feld steht
       CheckFieldName(field, s_staticFieldPrefix);
     }
     else
     {
       //Nun wird geschaut ob ein "nonStatic_" vor dem Feld steht
       CheckFieldName(field, s_nonStaticFieldPrefix);
     }
     return Problems;
   }
   private const string s_staticFieldPrefix = "static_";
   private const string s_nonStaticFieldPrefix = "nonStatic_";
   //Wenn CheckFieldName ein Problem findet (also der string nicht dem entspricht, was in expectedPrefix steht), wird ein "Problem" von Typ ProblemCollection erzeugt.
   private void CheckFieldName(Field field, string expectedPrefix)
   {
     if (!field.Name.Name.StartsWith(expectedPrefix, StringComparison.Ordinal))
     {
       Resolution resolution = GetResolution(field, // Field {0} entspricht nicht der gewünschten Notation.
               expectedPrefix // Field Name sollte eigentlich beginnen mit: {1}.
       );
       Problem problem = new Problem(resolution);
       Problems.Add(problem);
     }
   }
 }
}
  1. Wenn wir nun das Projekt erstellen, finden wir unsere gewünschte *.dll-Datei (im Verzeichnis: MyCustomFxCopRules\MyCustomFxCopRules) und kopieren diese in das VS 2010-Regel- Verzeichnis (C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\Rules bei 64-bit Systemen und C:\Program Files\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\Rules bei 32-bit Systemen).
  2. Nach einem Neustart von VS 2010 öffnen wir nun unser eigentliches Projekt, auf welches unsere Regel angewendet werden soll. Hier öffnen wir wieder unser vorhin erstellten Regelsatz (unser Beispiel: „MyCustomRuleSet“).
  3. Nun sollte die erstellte Regel ganz unten unter „MyRules“ angezeigt werden. Wichtig ist, dass nicht aktivierte Regeln angezeigt werden (siehe Bild).Bild 2: Eigene Regel
  4. Selektieren wir unsere hinzugefügte Regel und speichern den Regelsatz, wird unsere Regel bei der Codeanalyse angewendet.

Quellen:

Verfasst von: bletra | 21. Juni 2011

Facebook-Pinnwandeintrag mit einer C# WPF-Desktopanwendung

Autoren: 2 Studierende der Veranstaltung .Net Framework und C#

Facebook wird immer beliebter und immer mehr Facebook-Apps sprießen aus dem Boden. Jetzt stellt sich die Frage: Wie kann ich meine C#-WPF-Desktopanwendung mit der Facebook API erweitern, um z.B. einen Eintrag auf der eigenen Pinnwand zu veröffentlichen? Die Antwort darauf wird in diesem Blogeintrag gegeben!

Verwendet wird dabei das „Facebook C# SDK V5.0“, welches auf http://facebooksdk.codeplex.com/ kostenlos zur Verfügung steht. Bevor es aber losgehen kann, muss man eine Anwendung bei Facebook registrieren. Dies funktioniert auf folgendem Formular: http://www.facebook.com/developers/createapp.php. Eine Anmeldung ist nur mit einem bestätigten Facebook-Account möglich. Dazu muss man seine Handynummer oder Kreditkarte angeben. Anschließend erhält man die, für die Entwicklung benötigte „Anwendungs-ID“. Damit ist es aber noch nicht genug, denn um auf die Pinnwand eines Benutzers schreiben zu können, muss er dies explizit erlauben. Diese Erlaubnis ist in einem so genannten „Access-Token“ verkörpert. Um das Access-Token zu erhalten, muss der Nutzer in der Anwendung erst auf eine Facebook-Webseite geleitet werden, auf der er die Nutzung seines Accounts mit der App freigibt. Dies lässt sich mit einem eingebetteten Webbrowser in der Oberfläche bewerkstelligt. Die URL setzt sich wie folgt zusammen:

string u = "https://graph.facebook.com/oauth/authorize?";

//Anwendungs-ID
u = u + "client_id=XXXXXXXXXXXXXXXXXXX";

//Berechtigung
u = u + "&scope=publish_stream";

//URL, die nach Aufruf angezeigt wird
u = u + "&redirect_uri=http://www.facebook.com/connect/login_success.html";

//Login-Typ
u = u + "&type=user_agent";
u = u + "&display=popup";

Quelle: http://frank-it-beratung.com/2011/01/29/tutorial-ein-facebook-pinnwandeintrag-mit-visual-basic-oder-c-teil-2/

Nachdem der Nutzer seine Eingaben gemacht hat, wird er von Facebook weitergeleitet. Anhand der Weiterleitungs-Url lässt sich der Erfolg erkennen. Enthält die URL den Parameter „access_token“, war dies erfolgreich. Mithilfe eines NavigationEvents lässt sich so das Access-Token bestimmen und zwischenspeichern:

void webBrowser1_Navigated(object sender, System.Windows.Navigation.NavigationEventArgs e)
{
  //der Access-Token hängt an der URL
  string q = e.Uri.ToString();
  string txtAccessToken;

  //jetzt auslesen
  if (q.Contains("#access_token="))
  {
    q = q.Substring(q.IndexOf("access_token=") + 13);
    txtAccessToken = q.Substring(0, q.IndexOf("&"));
    hauptFenster.Fb.tmpAccessToken = txtAccessToken;
    this.Close();
  }
}

Nachdem das „Access-Token“ beschafft wurde, kann mit der Verwendung des Facebook C# SDK  begonnen werden. Hierzu erstellt man sich erst einen neuen FacebookClient, in dem das AccessToken gesetzt werden muss.

Facebook.FacebookClient client = new FacebookClient(tmpAccessToken);

client.AccessToken = tmpAccessToken; //Access-Token setzen

Nun kann man darüber Befehle absetzen. In unserem Fall das Posten auf die eigene Pinnwand.


Für einen Post auf die Pinnwand können verschiedene Parameter in einem „System.Dynamic.ExpandoObject“ gesetzt werden. Darunter fallen z.B. der Text des Posts, eine Url zu einem Bild, Titel, externer Link,… Alle möglichen Parameter finden sich hier: http://developers.facebook.com/docs/reference/api/post/. Anschließend wir das Ganze an die eigene Pinnwand (entspricht https://graph.facebook.com/me/feed) gesendet. Im Folgenden ist ein Beispiel für einen Spritrechner gegeben:

dynamic par = new System.Dynamic.ExpandoObject();

par.message = message;
par.picture = "http://dummyimage.com/600x300/3B5998/fff.jpg&text=" + averageConsuption + " l/100km";
par.name = "Durchschnittsverbrauch";
par.link = "http://www.XXXXXXXXXXXXXXXXXXXXXXXXXXXXX.com“;
par.description = averageConsuption + " l/100km";

par.privacy = new
{
  value = "ALL_FRIENDS",
};

client.PostAsync("https://graph.facebook.com/me/feed", par);

Und so sieht das Ergebnis auf Facebook aus:

Oberfläche FaceSprit

Viel Spaß beim Experimentieren mit der API!

Verfasst von: bletra | 20. Juni 2011

Kopfrechentrainer mit XNA

Gerne möchte ich auf einen Blogeintrag eines Studenten der Veranstaltung .Net Framework und C# aufmerksam machen, der einen Kopfrechentrainer für Kinder mit XNA implementiert hat: Ein eigenes Spiel mit XNA.

Verfasst von: bletra | 20. Juni 2011

Zugriff auf eine MySQL-Datenbank mittels C#

Autoren: 2 Studierende der Veranstaltung .Net Framework und C#

Für kleine Programme und vor allem auch für Webanwendungen wie Silverlight ist es oft ausreichend und sinnvoll, eine MySQL-Datenbank anstatt eines großen MS-SQL Servers zu verwenden. Außerdem haben viele PHP- und Java-Entwickler bereits Erfahrung mit MySQL und müssen sich nicht in ein neues DBMS einarbeiten. Da die Verwendung von MySQL im .NET Framework standardmäßig nicht unterstützt wird, kann es bei der Einrichtung zu ein paar kleinen Problemen kommen. Aus diesem Grund erklären wir im Folgenden kurz und knapp wie man aus seiner C# Anwendung auf eine MySQL-Datenbank zugreifen kann.

  1. Zunächst muss man den ADO.NET Treiber für MySQL herunterladen. Dieser ist kostenlos bei MySQL unter folgender Adresse zu erhalten: http://dev.mysql.com/downloads/connector/net/
  2. Unter Windows am besten die Windows Installer Version herunterladen. Bei der Installation ist nichts Weiteres zu Beachten und kann somit einfach „durchgeklickt“ werden.
  3. Starten Sie Visual Studio (hier 2010) und legen Sie Ihr gewünschtes Projekt an.
  4. Klicken Sie im Solution Explorer mit der rechten Maustaste auf „References“ (in einer Silverlight-Anwendung natürlich im Server-Teil des Projekts) und wählen „Add Reference…“ aus.
  5. Öffnen Sie den Tab „Browse“ und wechseln in das Verzeichnis, in das Sie den MySQL-Connector installiert haben und wählen Sie im Unterverzeichnis „Assemblies\v2.0“ die Datei MySql.Data aus.
  6.  Im Folgenden ein Ausschnitt einer Klasse der zeigt, wie man nach Durchführung der Punkte 1 bis 5 auf eine MySQL-Datenbank zugreifen kann. Hier am Beispiel eines Webservices einer Silverlight-Applikation.
public class Service
{
  private MySqlConnection db;
  public Service()
  {
    db = new MySqlConnection("SERVER=;DATABASE=;UID=;PASSWORD=;");
    db.Open();
  }

  [OperationContract]
  public Boolean Login(String email, String passwd) {
  byte[] data = Encoding.Default.GetBytes(passwd);
  using (MD5 algorithm = MD5.Create())
  {
    String passwdHashed = BitConverter.ToString(algorithm.ComputeHash(data));

    MySqlCommand command = db.CreateCommand();
    command.CommandText = "SELECT vvorname, nachname FROM mitglied WHERE email='" + email + "' AND passwort='" + passwdHashed + "' AND rechte='admin'";

    using (MySqlDataReader reader = command.ExecuteReader())
    {

      while (reader.Read())
      {
        // hier kann man nun auf die selektieren Spalten zugreifen
        // z.B: reader[`vorname`]
      }

      reader.Close();

…
    }
  }
}

Quelle:
http://code-inside.de/blog/2007/11/27/howto-datenbankverbindungen-net-mysql/

Verfasst von: bletra | 19. Juni 2011

Über die Einfachheit von Feeds-Verarbeitung in C#

Autor: D. Büchsel

Immer mehr Seiten im Netz unterstützen sogenannte (News-)Feeds. Die Vorteile liegen auf der Hand: Durch Feeds, seien es nun RSS- oder Atom-Feeds, lassen sich, insbesondere Newsseiten, einfach und unkompliziert überwachen und über etwaige  Änderungen informieren. Da ist es nicht verwunderlich, dass uns .NET hierfür eine gute, und vor allem eine einfache Schnittstelle für diese Aufgaben bietet.

Im Folgenden möchten wir ein kleines Beispiel zum Thema Feeds behandeln, um zu zeigen wie einfach die Nutzung der entsprechenden Klassen ist. In unserem Beispiel möchten wir den RSS Feed des Fachbereichs Informatik der Hochschule Darmstadt [1] abfragen und in einer Liste speichern und ausgeben.

Herzstück unseres Programms sind die Syndication-Klassen[2][3], sowie die WebClient-Klasse[4], welche uns die nötigen Funktionalitäten bietet, um Atom1.0- sowie RSS2.0-Feeds zu verarbeiten. Damit wir die Syndication-Klassen nutzen können, müssen wir vorher die entsprechende Referenz einbinden. Dies ist die System.ServiceModel (in System.ServiceModel.dll). Für unser Beispiel benötigen wir zudem die Using-Direktiven für System.Collections.Generic, System.IO, System.Net, System.ServiceModel.Syndication und System.Xml.

Nun aber zu unserem Beispiel: Unser erster Schritt zur Verarbeitung der Feeds ist sicherlich überhaupt erst einmal die Feeds herunterzuladen. Wobei wir von einem Download abstrahieren möchten und das Einlesen der Datei als Stream bevorzugen. Das bietet nicht nur den Vorteil, dass wir die Datei nicht auf unserer Festplatte zwischenspeichern müssen, sondern bietet auch die Möglichkeit eines kürzeren und schöneren Codes.

Uri url = new Uri("http://www.fbi.h-da.de/index.php?id=1955&type=100"); //Url zu Feed

WebClient request = new WebClient(); //stellt die Verbindung zur Webseite her
Stream stream = request.OpenRead(url); //öffnet Url als Stream
using (StreamReader reader = new StreamReader(stream)) //öffnen eines Readers auf dem Stream
{
    //TODO: Hier möchten wir unsere Feeds parsen
}

Wir haben nun einen StreamReader für unseren Feed (in diesem Fall ein RSS-Feed) geöffnet und können diesen nun an unseren XmlReader weitergeben. Der XmlReader stellt, wie der Name schon sagt, einen weiteren Reader zur Verfügung (noch haben wir nichts gelesen!). Den XmlReader übergeben wir wiederum als Parameter an die statische Load-Funktion von SyndicationFeed. Diese bietet uns als Rückgabe eine Instanz von SyndicationFeed, welche uns Zugriff auf die runtergeladenen Feeds bietet. Mittels Items-Property bekommen wir Zugriff auf die Feeds und können nun beliebige Operationen damit durchführen. Um das Beispiel nicht unnötig aufzublasen, fügen wir das Item nur einer Liste hinzu ohne weitere Operationen auszuführen. Hier könnten wir bspw. nur Feeds einfügen, die ein bestimmtes Keyword beinhalten oder von einem bestimmten Autor verfasst wurden.

Uri url = new Uri("http://www.fbi.h-da.de/index.php?id=1955&type=100"); //Url zu Feed

List<SyndicationItem> collection = new List<SyndicationItem>();

WebClient request = new WebClient(); //stellt die Verbindung zur Webseite her
Stream stream = request.OpenRead(url); //öffnet Url als Stream
using (StreamReader reader = new StreamReader(stream)) //öffnen eines Readers auf dem Stream
{
    XmlReader xmlReader = XmlReader.Create(reader); //öffnet einen Reader zum XML parsen
    SyndicationFeed feed = SyndicationFeed.Load(xmlReader); //öffnet Parser für Feeds vom XML Reader

    //parsen:
    foreach (SyndicationItem item in feed.Items)
    {
        collection.Add(item);
    }
}

Schlussendlich möchten wir von unseren Feeds auch mal etwas sehen. SyndicationItem bietet uns hier einige Propertys um auf dessen Feed-Attribute zuzugreifen. Sind die entsprechenden Attribute nicht verfügbar, gibt das Property null zurück. In unserem Beispiel möchten wir den Titel (der sollte im Allgemeinen immer verfügbar sein) und wenn verfügbar, dessen Id auf der Konsole ausgeben. Eine vollständige Liste aller Propertys ist unter [3] zu finden.

Uri url = new Uri("http://www.fbi.h-da.de/index.php?id=1955&type=100"); //Url zu Feed

List<SyndicationItem> collection = new List<SyndicationItem>();

WebClient request = new WebClient(); //stellt die Verbindung zur Webseite her
Stream stream = request.OpenRead(url); //öffnet Url als Stream
using (StreamReader reader = new StreamReader(stream)) //öffnen eines Readers auf dem Stream
{
    XmlReader xmlReader = XmlReader.Create(reader); //öffnet einen Reader zum XML parsen
    SyndicationFeed feed = SyndicationFeed.Load(xmlReader); //öffnet Parser für Feeds vom XML Reader

    //parsen:
    foreach (SyndicationItem item in feed.Items)
    {
        collection.Add(item);
    }
}

…

foreach (SyndicationItem item in collection)
{
    if (item.Id != null)
        System.Console.Write(item.Id + ": ");
    System.Console.WriteLine(item.Title.Text);
}

Wie man sehen kann, ist die Verarbeitung von RSS Feeds unter .NET im Vergleich mit anderen Lösungen (z. B. unter C++) kinderleicht. Dass ATOM- und RSS-Feeds dabei auf die gleiche Weise verwendet werden können, setzt dem ganzen noch ein i-Tüpfelchen oben drauf und bietet eine Menge Einsatzmöglichkeiten.

Referenzen:
[1] http://www.fbi.h-da.de/index.php?id=1955type=100
[2] http://msdn.microsoft.com/en-us/library/system.servicemodel.syndication.syndicationfeed.aspx
[3] http://msdn.microsoft.com/en-us/library/system.servicemodel.syndication.syndicationitem.aspx
[4] http://msdn.microsoft.com/en-US/library/system.net.webclient%28v=VS.80%29.aspx

Verfasst von: bletra | 18. Juni 2011

Selektion einer Datenmenge innerhalb von Crystal-Reports

Autoren: 2 Studierende der Veranstaltung .Net Framework und C#

Oft ist es notwendig, innerhalb einer Tabelle einer Datenbank eine Auswahl zu treffen, welche der betroffenen Datensätze denn nun wirklich in Crystal-Reports angezeigt werden soll. Im folgenden Beispiel verwenden wir als Ausgangsbasis eine Datenbank mit drei Tabellen, in denen Adressen, Adressgruppen-Namen und deren Zuordnung zueinander gespeichert sind. Die Namen der einzelnen Adress-Gruppen werden zu Beginn innerhalb eines Forms in eine Combo-Box geschrieben. Aufgrund dieser Auswah wird dann automatisch gefiltert.
Gehen Sie, wie folgt vor:
Klicken Sie im Report-Steuerelement mit der rechten Maustaste, wählen Sie aus: Bericht -> Datensatz -> Auswahlformel
Es erscheint folgendes Fenster:

Geben Sie ein: <tabellenname.spaltenname> = <?Parameter>
Nun müssen Sie nur noch über den Quelltext diesen Parameter füllen und dies geht, wie folgt:

ParameterFieldDefinitions crParameterFieldDefinitions;
ParameterFieldDefinition crParameterFieldDefinition;
ParameterValues crParameterValues = new ParameterValues();
ParameterDiscreteValue crParameterDiscreteValue = new ParameterDiscreteValue();
crParameterDiscreteValue.Value = comboBox1.SelectedItem.ToString();
crParameterFieldDefinitions = cryRpt.DataDefinition.ParameterFields;
crParameterFieldDefinition = crParameterFieldDefinitions["GruppeName"];
crParameterValues = crParameterFieldDefinition.CurrentValues;
crParameterValues.Clear();
crParameterValues.Add(crParameterDiscreteValue);
crParameterFieldDefinition.ApplyCurrentValues(crParameterValues);

cryRpt ist das Objekt auf Basis eines Reports, der zuvor, wie folgt deklariert und an die Datenbank angebunden wurde:

ReportDocument cryRpt = new ReportDocument();
cryRpt.Load(@"CrystalReport4.rpt");
cryRpt.SetDatabaseLogon(BenutzerName,PassWord,ServerAdresse,DatenBankName);

Abschließend übergeben wir dann alles dem Crystal-Reports-Control und führen einen Refresh durch:

crystalReportViewer1.ReportSource = cryRpt;
crystalReportViewer1.Refresh();

Das war es! Unser Report kann nun gefiltert über eine Spalte seine Daten bekommen.

Bei der Einarbeitung hat uns folgende Seite geholfen:

Verfasst von: bletra | 17. Juni 2011

WebRequest und WebResponse

Autoren: Marius Kolkowski und Jan Drietelaar

WebRequest ist eine abstrakte Basisklasse die zum Anforderungs-/Antwortmodell von .Net gehört. Sie ist ab .Net Framework 2.0 verfügbar und ist vorerst protokoll-unabhängig. Klassen, die von dieser erben, können auf die verschiedenen Protokolle genauer eingehen. Um eine neue Instanz von WebRequest zu erstellen, sollte man die Funktion Create() benutzen. Hier wird dann der Eingabestring (die abzufragende URI) auf ihr Protokoll untersucht und eine Instanz einer abgeleiteten Klasse zurückgegeben. Sollte beispielsweise die Webseite http://www.google.de abgefragt werden, dann liefert uns die Funktion Create() des WebRequests ein Objekt einer davon abgeleiteten Klasse HttpWebRequest. Wollen wir jetzt eine Antwort der URI erhalten, können wir per GetResponse() diese protokollspezifisch bekommen. Dabei wird ein Instanz der Klasse WebResponse zurückgegeben — genauer ein HttpWebResponse-Objekt; denn auch hier ist es so, dass WebResponse eine abstrakte Basisklasse ist, welche protokollunabhängig funktioniert. Das alles hat den Sinn, um erst während der Laufzeit auf die Protokollart eingehen zu müssen. Nachdem wir unsere Antwort erhalten haben, können wir diese weiterverarbeiten und z.B. ausgeben lassen.

// Erstellen eines WebRequests. Die Create-Methode gibt in diesem Fall ein HttpWebRequest-Objekt zurück.
HttpWebRequest req = WebRequest.Create("http://www.google.de");
// Die Antwort der URI wird in ein WebResponse-Objekt geladen und kann hier weiterverarbeitet werden. Auch hier wird ein Abgeleitetes WebResponse Objekt erstellt (ein HttpWebResponse Objekt).
WebResponse resp = req.GetResponse();
// Hier ein Beispiel, wie das WebResponse Objekt in eine string Variable weiterverarbeitet werden kann.
StreamReader sr = new StreamReader(resp.GetResponseStream());
// In unserem Beispiel enthält die string Variable jetzt den HTML Code der Webseite Google.
string htmlCode = sr.ReadToEnd();

Verfasst von: bletra | 15. Juni 2011

Asp.Net: Benutzerverwaltung

Verfasst von: bletra | 14. Juni 2011

XNA: Einführung und Wandering Steering Behaviour

Autor: David Reinig

Vorwort

Im Rahmen des Praktikums, der Lehrveranstaltung C# und .NET, wurde die Aufgabe gestellt ein Programmierprojekt selbstständig zu erstellen, wobei hier die Konzentration auf einer bestimmten (beliebig wählbaren) Microsoft Technologie lag.
Ich entschied mich für XNA, einer Technologie die von Microsoft speziell für die Entwicklung von Spielen bereit gestellt wird.
Das Projekt sollte ein 2D-Spiel, mit bekanntem Spielprinzip (Bubbles, Bubblet, …) werden und einige der angebotenen XNA Funktionalitäten ausnutzen.
Dieser Artikel umreist im ersten Abschnitt sehr grob die Grundlagen der Entwicklung mit XNA. Im zweiten Abschnitt wird auf ein Teilthema eingegangen das im entwickelten Spiel eine Rolle spielt.

2D Grafik mit XNA

Grundlagen

Da in meinem Projekt 2D-Grafik als Darstellung verwendet wird, werden hier die entsprechenden Grundlagen kurz aufgeführt.
Jedes XNA Projekt besitzt als Einstiegspunkt eine Instanz der Klasse Game. Diese Klasse stellt alle notwendigen Schnittstellen zur Steuerung der Spiellogik, das Zeichnen des Outputs usw. zur Verfügung. Der Einstiegspunkt ist die Methode Run. Diese initialisiert zunächst das Spiel (Initialize), bei diesem Vorgang besteht die Möglichkeit, notwendige Ressourcen zu laden (LoadContent).
Nach dem Initialisierungsvorgang befindet sich das Spiel in der „Gameloop“, wobei von dort frequentiell die Methoden Update und Draw aufgerufen werden. Erstere dient zum Updaten der Spiel-Logik, letztere um den grafischen Output zu realisieren.
Ein grundlegendes Spielgerüst sähe z.B. aus wie folgt:


public class Game1 : Microsoft.Xna.Framework.Game
{
  GraphicsDeviceManager graphics;

  public Game1()
  {
    graphics = new GraphicsDeviceManager(this);
    Content.RootDirectory = "Content";
  }

  protected override void Initialize()
  {
    base.Initialize();
  }

  protected override void LoadContent(){}
  protected override void UnloadContent(){}
  protected override void Update(GameTime gameTime){}

  protected override void Draw(GameTime gameTime)
  {
    GraphicsDevice.Clear(Color.Black);
  }
}

Game Content & Sprites zeichnen

XNA bietet eine komfortable Unterstützung um Game Content in das Spiel zu integrieren. Hierzu zählen z.B. eine Hand voll Default-Loader für diverse Ressourcen-Formate (bmp, dds, wav, …) das Management der Ressourcen und Content Prozessoren (z.B. um Bitmap-Fonts voll automatisch zu interpretieren).
Der verwendete Content kann im Solution Explorer unter {Projektname}Content eingesehen und dessen Eigenschaften bearbeitet werden. Um z.B. ein Sprite in das Spiel zu integrieren, kann die Grafikdatei einfach auf den Content Zweig gezogen werden (alternativ: Rechtsklick -> Add -> Existing Item…).
Sind die Ressourcen so bekannt gemacht, können diese im Spiel geladen und verwendet werden. Um Beispielsweise ein Sprite zu rendern, muss dieses zunächst in LoadContent geladen werden.

protected override void LoadContent()
{
  mSpriteBatch = new SpriteBatch(GraphicsDevice);
  mSprite = Content.Load("Bubble");
}

Wie hier zu erkennen ist, wurde zusätzlich ein SpriteBatch erzeugt. Dieses Objekt ermöglicht das Zeichnen von Grafiken.

protected override void Draw(GameTime gameTime)
{
  GraphicsDevice.Clear(Color.Black);
  try
  {
    mSpriteBatch.Begin();
    mSpriteBatch.Draw(mSprite, new Vector2(100, 100), Color.White);
  }
  finally
  {
    mSpriteBatch.End();
  }
}

Die Methode Draw bietet eine Vielzahl von Parametern. Hier können die gezeichneten Grafiken u. A. skaliert, rotiert, eingefärbt oder nur ein bestimmter Teil der Quellgrafik gezeichnet werden.

Steering Behaviour – Wandernde „Bubbles“

Grundlagen

Nachdem die Grundfunktionalität des Spiels entwickelt wurde, mussten im Zuge des grafischen „Feintunings“ Ideen her, um das Spiede „aufzupeppen“ und den Inhalt interessanter zu präsentieren. Der schwarze Hintergrund des Menüs sollte weg und mit einigen Bubbles versehen werden, die ziellos über den Bildschirm wandern.
Um dies zu realisieren, wurde ein Algorithmus aus dem Bereich der AI (Artificial Intelligence) verwendet, spezieller Wandering Steering Behaviour. Die grundlegende Idee ist es ein Objekt in einer virtuellen Umgebung, möglichst lebensnah, ziellos herumlaufen zu lassen.

Abb 1: Bubbles Spielmenü mit Menü-Hintergrund

Der Algorithmus

Hierfür bekommt jedes Bubble, zusätzlich zu seiner aktuellen Position, eine Bewegungsrichtung. Um ein plötzliches ‚Umspringen‘ zu vermeiden kann jeder Bubble seine Richtung nur in einem bestimmten Bereich abändern. In diesem Bereich wird ein zufälliger Wert bestimmt und die Bewegungsrichtung entsprechend auf den neu bestimmten Punkt ausgerichtet.
Anschließend wird der Bubble mit normalen Steering auf den neuen Punkt zubewegt.

private void initializeSteering(ITransformable sprite)
{
  mWanderTheta = (Random.Next(0, 360) * Math.PI / 180.0);
  sprite.Position = new Vector2(Random.Next(mBounds.Left, mBounds.Right), Random.Next(mBounds.Top, mBounds.Bottom));
  mHeading = new Vector2((float)Math.Cos(mWanderTheta), (float)Math.Sin(mWanderTheta));
}

protected override void ApplyInternal(ITransformable sprite, double scaledTimeFactor)
{
  Vector2 pos;

  mWanderTheta += Random.Next(-1, 1) / 100.0;

  if (mWanderTheta > Math.PI * 2)
    mWanderTheta -= Math.PI * 2;

  pos = sprite.Position;
  mHeading.X = (float)Math.Cos(mWanderTheta);
  mHeading.Y = (float)Math.Sin(mWanderTheta);
  pos = Vector2.Add(pos, new Vector2(mHeading.X * mSpeed, mHeading.Y * mSpeed));
  sprite.Position = pos;
}

Übermütige Wanderer

Einige Bubbles könnten so allerdings auf die Idee kommen sich aus dem Bildschirmbereich zu entfernen und nicht wieder zurück kommen. Daher wurden zwei Strategien implementiert um dies zu vermeiden:

  • Wrap Around

Beim Übertreten der gegebenen Grenzen erscheint das Bubble an der gegenüberliegende Grenze wieder.

  • Stay In Bounds

Bubbles prüfen ob sie die Grenzen überschreiten würden und ändern entsprechend ihre Position um dies zu verhindern.
Bevor also die Sprite Position zugewiesen wird muss diese entsprechend vorbereitet werden:

if (mWrapType == WrapType.WrapAround)
{
  if (pos.X > mBounds.Right)
    pos.X = mBounds.Left;
  if (pos.X < mBounds.Left)     pos.X = mBounds.Right;   if (pos.Y > mBounds.Bottom)
    pos.Y = mBounds.Top;
  if (pos.Y < mBounds.Top)     pos.Y = mBounds.Bottom; } else {   if (pos.X > mBounds.Right || pos.X < mBounds.Left || pos.Y > mBounds.Bottom || pos.Y < mBounds.Top)
    mWanderTheta += 0.1 + (Math.PI / 2);
}
Verfasst von: bletra | 8. Juni 2011

WPF mit mehreren Fenstern starten

Die Projektvorlage für WPF im Visual Studio generiert ein leeres Fenster (MainWindow.xaml) und eine Datei App.xaml. Die Applikation startet standardmäßig mit dem erzeugten Fenster und beendet sich, wenn dieses geschlossen wird. Schaut man in den Code von App.xaml, so findet man die Anweisung StartupUri=“MainWindow.xaml“, was das Start-Verhalten erklärt. Ein Student wollte jedoch die Applikation mit einem Konfigurationsfenster starten und anschließend weitere Fenster öffnen. Schnell fanden wir heraus, wie man eine Methode, ähnlich einer Main-Methode,  definieren kann, die bei Applikationsstart ausgeführt wird. Hierzu muss in App.xaml anstelle der StartupUri das Attribut Startup wie folgt gesetzt werden: Startup=“main“. Diese Methode ist dann in App.xaml.cs zu implementieren. Hier folgte nun das zweite Problem, das Konfigurationsfenster konnte er anzeigen lassen, weitere Fenster jedoch nicht und die Applikation wurde beendet. Der Grund liegt darin, dass standardmäßig die Applikation beendet wird, sobald das letzte Fenster geschlossen wird. Diese Applikationseigenschaft lässt sich durch den ShutdownMode  entweder in App.xaml oder im Code verändern. Folgende Modi werden unterstützt: OnLastWindowClose (default), OnMainWindowClose und OnExplicitShutdown. Wählt man OnExplicitShutdown, so kann an beliebiger Stelle im Code mit App.Current.Shutdown() das Beenden der Applikation erfolgen.

Hier nun eine Beispielapplikation, die die Möglichkeiten verdeutlicht. Zunächst erscheint ein Config-Fenster mit der Möglichkeit, die Applikation zu beenden (btnExit). Anschließend werden zwei Fenster geöffnet, von denen eines als MainWindow deklariert wird — dies erfolgte nur zu Demonstrationszwecken und ist eigentlich nur in Verbindung mit OnMainWindowClose relevant. Die Applikation soll sich beenden, wenn das letzte Fenster geschlossen wird. Dabei ergab sich ein weiteres Problem, obwohl im Debugger eine statische Eigenschaft IsShuttingDown der Klasse Application angezeigt wird, gibt es hierzu keine Dokumentation und diese Eigenschaft ist nicht verfügbar. Daher wird über das Attribut Exit in der App.xaml eine Methode angegeben, die bei Eintreten des Exit-Events ein solches Property der Klasse App explizit setzt. Eine gute Übersicht über den Lebenszyklus einer Applikation bietet Microsoft in „Übersicht über die Anwendungsverwaltung“.

App.xaml:

<Application x:Class="test.App"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  Startup="main"
  ShutdownMode="OnExplicitShutdown"
  Exit="Application_Exit"
>
  <Application.Resources></Application.Resources>
</Application>

App.xaml.cs

public partial class App : Application
{
  private bool isShuttingDown;
  public bool IsShuttingDown { get { return isShuttingDown; } }
  public void main(object sender, StartupEventArgs startupEventArgs)
  {
    isShuttingDown = false;
    Config config = new Config();
    config.ShowDialog();
    if (!isShuttingDown)
    {
      MainWindow mainWin = new MainWindow();
      App.Current.MainWindow = mainWin;
      mainWin.Show();
      AddOn addOnWin = new AddOn();
      addOnWin.Show();
      App.Current.ShutdownMode = ShutdownMode.OnLastWindowClose;
    }
  }
  private void Application_Exit(object sender, ExitEventArgs e)
  {
    isShuttingDown = true;
  }
}

Config.xaml.cs

private void btnExit_Click(object sender, RoutedEventArgs e)
{
  App.Current.Shutdown();
}

Der dargestellte Ansatz lässt sich auch leicht auf ein Login-Fenster übertragen.

Der Bayerische Rundfunk hat eine multimediale Lernumgebung zu Mathe, Englisch und Deutsch online zur Verfügung gestellt: GRIPS. Zielgruppe sind Hauptschüler. Ich habe mir die Einheit „Grundlagen der Bruchzahlen“ angesehen. Die Videosequenzen sind ansprechend und mit Sprüchen aufgelockert; als Aufhänger werden Rezepte gewählt. Die Lerneinheiten eignen sich sicher auch für Fünft- oder Sechstklässler des Gymnasiums. Oder wie der Bildungsserver dazu meint, für Eltern 🙂

Verfasst von: bletra | 19. Mai 2011

Artikelempfehlung: Wissenschaftliches Arbeiten

Die Zeit nahm die Plagiatsvorwürfe zum Anlass und hat einen aktuellen Artikel mit vielen interessanten Links zum Thema „wissenschaftliches Arbeiten“ geschrieben. Es gibt Links zu den Themen:

  • Kriterien guter wissenschaftlicher Arbeit
  • Zitieren
  • Literaturrecherche
  • Finden und Benutzen von Quellen
  • Wie gehe ich mit Bildquellen um?
  • Statistiken lesen und sinnvoll nutzen

Ich denke, mit den Themen kann man sich gar nicht früh genug im Studium beschäftigen. Gerade bei der Literaturrecherche lohnt es sich am Ball zu bleiben und neue Suchmöglichkeiten zu nutzen.

Verfasst von: bletra | 14. Mai 2011

WPF: Von Spaghetticode zu MVVM – Teil 9 von 9

In dieser Artikelserie habe ich eine einfache Applikation, in eine testbare Applikation nach dem MVVM-Pattern umgewandelt:

  1. Beispielapplikation mit Spaghetticode
  2. Refactoring 1: Model
  3. MVC, MVP, Presentation Model
  4. MVVM
  5. ICommand
  6. Binding
  7. Refactoring 2: ViewModel
  8. Refactoring 3: View
  9. Zusammenfassung (dieser Artikel)

Zusammenfassung

Mit dem MVVM-Architekturpattern können View und Model leicht ausgetauscht werden. D.h. sowohl die im ViewModel implementierte Präsentationslogik als auch die Geschäftslogik des Models können voneinander unabhängig automatisch getestet werden. Designer können die Gestaltung der View übernehmen und Entwickler implementieren die Präsentationslogik. Der CodeBehind-Anteil der View wird so reduziert. Es ist im Einzelfall jedoch abzuwägen, ob eine Implementation im CodeBehind-Teil nicht der teilweise umständlichen Auslagerung ins ViewModel vorzuziehen ist. Die View wird über Änderungen im ViewModel informiert. Das Model greift häufig auf Daten in einer Datenbank zu. Werden diese Daten mit einer anderen Applikation geändert, so bekommt dies die MVVM-Applikation erst bei der nächsten Datenabfrage mit.

Bei komplizierteren Oberflächen werden Sie dieses Pattern nicht wie in der hier betrachteten einfachen Beispielapplikation auf das gesamte Fenster anwenden, sondern die View aus verschiedenen UserControls zusammensetzen. Jedes UserControl ist dabei mit Verhalten und Daten nach dem MVVM-Pattern implementiert, d.h. der DataContext eines UserControls ist dann ein dazu passendes ViewModel.

Diese Artikelserie hat Ihnen die Grundlagen des MVVM-Patterns anhand der Umwandlung einer einfachen Demo-Applikation Schritt für Schritt aufgezeigt. Einige Codezeilen wiederholen sich in jeder MVVM-Applikation. Außerdem sind Konzepte zur Kommunikation zwischen Fenstern und Dialogen wünschenswert. Diese Probleme adressieren Frameworks. Da Microsoft keine MVVM-Vorlage mit VS ausliefert, gibt es unterschiedliche Open Source Frameworks — Microsoft selbst arbeitet an Prism. Die in dieser Artikelserie dargestellten Prinzipien sollten es Ihnen jedoch ermöglichen, sich in diesen Frameworks gut orientieren und sie somit effizient nutzen zu können.

Fazit

  • Das MVVM-Pattern Ist ein weit verbreitetes Muster der WPF- und Silverlight-Entwicklung.
  • Das MVVM-Pattern ist in verschiedenen Frameworks (Prism, MVVM Light, … ) unterschiedlich implementiert.
  • Das Model kapselt die Datenschicht und Geschäftslogik – analog zum Model des MVC-Patterns.
  • Die Methoden und Eigenschaften des Models werden in einem Interface definiert.
  • Das ViewModel …
    • bekommt im Konstruktor das Model (Type ist das zugehörige Interface) – Stichwort: Dependency Injektion,
    • ruft Änderungsmethoden des Models auf,
    • kennt die View nicht,
    • speichert den Zustand der View (über INotifyPropertyChanged),
    • stellt das Präsentationsverhalten in Form von Commands (ICommand) zur Verfügung.
  • Die View …
    • wird in XAML definiert,
    • DataContext ist eine Instanz von ViewModel,
    • wenig CodeBehind-Anteil,
    • kennt das Model nicht,
    • die Bindung von Eigenschaften (DependencyProperty) und Befehlen (Command) an das ViewModel wird ebenfalls in XAML definiert.
  • Das MVVM-Pattern führt zu einer abstrakteren Architektur und sollte bei Applikationen eingesetzt werden, für die man auch automatische Tests plant.

Den kompletten Quellcode der Demo-Applikation finden Sie unter: RefactorFeedManager.zip

Verfasst von: bletra | 13. Mai 2011

WPF: Von Spaghetticode zu MVVM – Teil 8 von 9

In dieser Artikelserie möchte ich eine einfache Applikation, in eine testbare Applikation nach dem MVVM-Pattern umwandeln:

  1. Beispielapplikation mit Spaghetticode
  2. Refactoring 1: Model
  3. MVC, MVP, Presentation Model
  4. MVVM
  5. ICommand
  6. Binding
  7. Refactoring 2: ViewModel
  8. Refactoring 3: View
  9. Zusammenfassung

View

In der View muss der DataContext, wie in Teil 6 beschrieben, auf eine Instanz des ViewModels gesetzt werden. Die View kann nun die Commands und Properties des ViewModels wie folgt nutzen:

<DockPanel Cursor="{Binding Path=Cursor, Mode=OneWay}">
    <Label DockPanel.Dock="Top" Content="New feed address" Height="28" Name="lblFeed"  />
    <TextBox DockPanel.Dock="Top" Name="txtFeedAddress" Text="{Binding Path=Address, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"  />
    <Button DockPanel.Dock="Top" Content="Add feed" Name="btnAddFeed" Command="{Binding Path=AddCmd}" />
    <StatusBar Height="40" DockPanel.Dock="Bottom">
      <Label Name="lblMsg" Content="{Binding Path=Msg, Mode=OneWay}" />
    </StatusBar>
    <Button DockPanel.Dock="Bottom" Content="save feed list" Name="btnSave" Command="{Binding Path=SaveCmd}" />
    <ListBox Name="lbFeeds" ItemsSource="{Binding Path=Feeds, Mode=OneWay}" />
</DockPanel>

Die View bekommt alle Änderungen des ViewModels mit. Im nächsten und letzten Artikel dieser Serie fasse ich das Vorgehen und die vorgestellten Techniken kurz zusammen.

Verfasst von: bletra | 12. Mai 2011

WPF: Von Spaghetticode zu MVVM – Teil 7 von 9

In dieser Artikelserie möchte ich eine einfache Applikation, in eine testbare Applikation nach dem MVVM-Pattern umwandeln:

  1. Beispielapplikation mit Spaghetticode
  2. Refactoring 1: Model
  3. MVC, MVP, Presentation Model
  4. MVVM
  5. ICommand
  6. Binding
  7. Refactoring 2: ViewModel
  8. Refactoring 3: View
  9. Zusammenfassung

ViewModel

Im letzten Artikel haben Sie bereits ein sehr einfaches ViewModel kennen gelernt. Welche Zustände und Befehle benötigt die Feed-Applikation?

  • Befehle:
    • AddFeed (enabled, falls im Adressfeld etwas eingegeben wurde)
    • Save (immer möglich)
  • Zustände/Properties:
    • Eine sortierte Liste der Titel der Feeds
    • Text im Adressfeld
    • Zustand des Cursors (hierfür führen wir eine weitere Eigenschaft ein: protected IsBusy)
    • Message, hier hinterlegen wir Fehler- und Statusmeldungen.

Das ViewModel kennt die View nicht, aber das Model. Dieses gestalten wir austauschbar über ein Interface – wie in Teil 3 beim MVP-Pattern dargestellt. Damit ergibt sich folgender Code:

public class FeedsViewModel : BaseViewModel
{
    private ICommand _addCmd;
    private ICommand _saveCmd;
    private Model.IFeedProvider _model;
    private string _msg;
    private string _address;
    private bool _isBusy = false;

    public FeedsViewModel()
        : this(new Model.FeedModel())
    {

    }

    public FeedsViewModel(Model.IFeedProvider model)
    {
        _model = model;
        _msg = "";
        _address = "";
        _addCmd = new DelegateCommand((param) => AddFeed(), (param) => _address.Length > 0);
        _saveCmd = new DelegateCommand((param) => Save());
    }

    public string Address
    {
        get { return _address; }
        set
        {
            _address = value;
            NotifyPropertyChanged("Address");
        }
    }

    public ICommand AddCmd
    {
        get { return _addCmd; }
    }

    public ICommand SaveCmd
    {
        get { return _saveCmd; }
    }

    public string Msg
    {
        get { return _msg; }
        protected set
        {
            _msg = value;
            NotifyPropertyChanged("Msg");
        }
    }

    public Cursor Cursor
    {
        get
        {
            if (_isBusy)
            {
                return Cursors.Wait;
            }
            return Cursors.Arrow;
        }
    }

public List Feeds
    {
        get
        {
            return _model.Feeds.Keys.OrderBy(k=>k).ToList();
        }
    }
    protected bool IsBusy
    {
        get { return _isBusy; }
        set
        {
            _isBusy = value;
            NotifyPropertyChanged("IsBusy");
            NotifyPropertyChanged("Cursor");
        }
    }
    private void AddFeed()
    {
        IsBusy = true;
        Msg = ""; //do not use private member to raise events for the view
        try
        {
            _model.AddFeed(_address);
            Address = "";
            NotifyPropertyChanged("Feeds");
        }
        catch (Exception ex)
        {
            Msg = ex.Message;
        }
        IsBusy = false;
    }
    private void Save()
    {
        try
        {
            _model.SaveFeeds();
            Msg = "Saved feeds successfully.";
        }
        catch (Exception ex)
        {

            Msg = "Error: " + ex.Message;
        }
    }
}

Mit diesem ViewModel können wir die ursprüngliche Funktionalität (etwas robuster) in den XAML-Code der View einbauen. Dies tun wir im nächsten Artikel.

Verfasst von: bletra | 11. Mai 2011

WPF: Von Spaghetticode zu MVVM – Teil 6 von 9

In dieser Artikelserie möchte ich eine einfache Applikation, in eine testbare Applikation nach dem MVVM-Pattern umwandeln:

  1. Beispielapplikation mit Spaghetticode
  2. Refactoring 1: Model
  3. MVC, MVP, Presentation Model
  4. MVVM
  5. ICommand
  6. Binding
  7. Refactoring 2: ViewModel
  8. Refactoring 3: View
  9. Zusammenfassung

Binding

Beim Binding geht es darum, ein GUI-Element an Daten oder einen Command eines anderen Objekts zu binden. Dieses andere Objekt kann auch ein GUI-Element sein. Im Folgenden betrachte ich zunächst den Fall der Datenbindung, am Ende des Artikels gehe ich kurz auf das Binden von Commands ein.

Bei der Datenbindung sollen Änderungen zwischen den gebundenen Objekten propagiert werden. Die Elemente der GUI bilden das Ziel (Target) und das andere Objekt die Quelle (Source). Sind beides GUI-Elemente, so ist das Element, bei dem ein Binding definiert wird das Ziel – die Bindung muss nicht doppelt definiert werden. Es sind vier verschiedene Modi zu unterscheiden:

  • OneWay: Änderungen der Quelle führen zu Änderungen im Ziel.
  • TwoWay (default): Änderungen warden in beide Richtungen propagiert.
  • OneWayToSource: Umgekehrt zu OneWay, Änderungen des Ziels führen zu Änderungen der Quelle.
  • OneTime: Bei der Initialisierung des Ziels werden die Daten der Quelle abgefragt, Änderungen werden nicht propagiert.

Die technologische Basis bilden entsprechende Events. Die Eigenschaft eines GUI-Elements, dessen Wert an die Eigenschaft (Property) eines anderen Objekts gebunden werden soll, muss ein sogenanntes DependencyProperty sein. Allgemein stellt ein DependencyProperty „eine Eigenschaft dar, die durch Methoden wie Formatierung, Datenbindung, Animation und Vererbung festgelegt werden kann.“ Bei der Bindung muss nun noch festgelegt werden, welches Ereignis des Ziels eine Mitteilung der Änderung an die Quelle (im Beispiel PropertyChanged) bewirkt. Das folgende Beispiel zeigt die Synchronisation zweier Textboxen:

<TextBox Name="txtTarget" Text="{Binding Mode=TwoWay, ElementNa-me=txtSource, Path=Text, UpdateSourceTrigger=PropertyChanged}" />
<TextBox Name="txtSource" />

Mit der Eigenschaft Path wird festgelegt, auf welche Eigenschaft von txtSource sich die Bindung bezieht. Dies muss nicht ebenfalls die Text-Eigenschaft sein, wie folgendes Beispiel zeigt:

<TextBox Name="txtTarget" Text="{Binding Mode=TwoWay, Element-Name=txtSource,Path=Background, UpdateSourceTrigger=PropertyChanged}" />
<TextBox Name="txtSource"  />
<Button Content="Red" Click="Button_Click" />

Mit CodeBehind

private void Button_Click(object sender, RoutedEventArgs e)
{
    txtSource.Background = new SolidColorBrush(Colors.Red);
}

Soll die Bindung zu einer Eigenschaft eines Nicht-GUI-Objekt erfolgen, so muss erstens dieses Objekt bekannt gemacht werden (ElementName funktioniert nun nicht): Hierfür gibt es verschiedene Möglichkeiten, in Bezug auf MVVM setzen wir die Eigenschaft DataContext des MainWindows/UserControls auf eine Instanz der Quelle. Auch hierfür gibt es wieder unterschiedliche Möglichkeiten, einfach kann dies nach InitializeComponent() erfolgen:

DataContext = new DemoViewModel();

Zweitens benötigen wir zur  Weiterleitung von Änderungen der Quelle (Property von DemoViewModel) an das Ziel (GUI) ebenfalls ein entsprechendes event. Dieses wird über das Interface INotifyPropertyChanged definiert:

public interface INotifyPropertyChanged
{
    event PropertyChangedEventHandler PropertyChanged;
}
public delegate void PropertyChangedEventHandler(object sender, PropertyChangedEventArgs e);

Als Basis für ein zu bindenden Objekt bietet sich folgende Klasse an:

public abstract class BaseViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    protected void NotifyPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

Im Setter und allen in anderen Methoden, die die zu bindende Eigenschaft (hier Greeting) verändert,  noch NotifyPropertyChanged aufgerufen werden, wie folgendes Beispiel zeigt:

public class DemoViewModel: BaseViewModel
{
  private string _greeting = "hi folks";
  public string Greeting
  {
    get { return _greeting; }
    set { _greeting = value; NotifyPropertyChanged("Greeting"); }
  }
}

Die Eigenschaft Greeting lässt sich nun wie folgt in XAML an die DependencyProperty Text einer Textbox binden:

<TextBox Name="txtTarget2" Text="{Binding Mode=TwoWay, Path=Greeting, Up-dateSourceTrigger=PropertyChanged}" PresentationTrace-Sources.TraceLevel="High" />

Mit der Eigenschaft PresentationTraceSources bekommen Sie ggf. mehr Debug-Informationen im Output-Window. Binding ist eine tolle Sache, aber lausig zu debuggen und damit ist es leider schwer, Fehler im Binding zu finden. Ein Blick in das Output-Fenster während des Debuggens ist jedoch immer ein Versuch wert.

Sie müssen nicht notwendigerweise selbst das Interface INotifyPropertyChanged implementieren und sich um das Auslösen des PropertyChanged-Events kümmern, für Listen können Sie beispielsweise die Klasse ObservableCollection verwenden, die das Interface bereits implementiert.

Merke: Die Datenbindung erfordert …

  • ein Objekt (Quelle) einer Klasse, die INotifyPropertyChanged implementiert
  • ein Objekt (Ziel, GUI) mit einem DependencyProperty

Diesen Zusammenhang veranschaulicht die folgende Abbildung, die aus dem Artikel „DataBinding in WPF“ von Christian Mosers stammt:

Zu sehen ist auch ein ValueConverter. Auf diesen können wir im Allgemeinen verzichten, da das ViewModel ggf. die Werte anpasst. Nun haben wir alle Technologien eingeführt und können im nächsten Artikel das ViewModel implementieren.

Verfasst von: bletra | 9. Mai 2011

WPF: Von Spaghetticode zu MVVM – Teil 5 von 9

In dieser Artikelserie möchte ich eine einfache Applikation, in eine testbare Applikation nach dem MVVM-Pattern umwandeln:

  1. Beispielapplikation mit Spaghetticode
  2. Refactoring 1: Model
  3. MVC, MVP, Presentation Model
  4. MVVM
  5. ICommand
  6. Binding
  7. Refactoring 2: ViewModel
  8. Refactoring 3: View
  9. Zusammenfassung

Commands

Es gibt eine Reihe von vordefinierten Commands in .Net:

Diese können einfach an ein Ereignis der GUI gebunden werden:

<MenuItem Command ="ApplicationCommands.Copy"/>

Hiermit lässt sich so ohne jegliche C#-Programmierung ein einfacher Editor implementieren (siehe WPF – A simple text Editor in less than 20 lines of markup!)

Die Basis dieser Commands sind ICommand und RoutedCommand, die von ICommand erbt. ICommand ist eine Schnittstelle des Namensraums System.Windows.Input und ist wie folgt definiert:

public interface ICommand
{
  bool CanExecute(Object parameter);
  void Execute(Object parameter);
  event EventHandler CanExecuteChanged;
}

Über CanExecute wird ein disable/enable des Commands und damit des diesen Commands nutzenden GUI-Elements (z.B. Button) definiert.

Man kann nun für jeden Command, den das ViewModel zur Verfügung stellt, eine eigene Klasse programmieren, die ICommand implementiert. Häufig kommt jedoch ein generischer Ansatz auf Basis von Delegates (Generische Delegates) zum Einsatz. In Anlehnung an die Artikel „ICommand is like a chocolate cake“  und „WPF-Anwendungen mit dem Model-View-ViewModel-Entwurfsmuster“ verwende ich folgende Basisklasse für Commands:

public class DelegateCommand : ICommand
{
  private readonly Action<object> _executeDelegate;
  private readonly Func<object, bool> _canExecuteDelegate;
  public DelegateCommand(Action<object> executeDelegate)
    : this(executeDelegate, null)  { }

  public DelegateCommand(Action<object> executeDelegate, Func<object, bool> canExecuteDelegate)
  {
    _executeDelegate = executeDelegate;
    _canExecuteDelegate = canExecuteDelegate;
  }
  public void Execute(object parameter)
  {
    _executeDelegate(parameter);
  }

  public bool CanExecute(object parameter)
  {
    return _canExecuteDelegate == null ? true : _canExecuteDelegate(parameter);
  }
  public event EventHandler CanExecuteChanged //see http://msdn.microsoft.com/de-de/magazine/dd419663.aspx#id0090030
  {
    add { CommandManager.RequerySuggested += value; }
    remove { CommandManager.RequerySuggested -= value; }
  }
}

Durch die Weiterleitung von CanExecuteChanged an CommandManager.RequerySuggested wird sichergestellt, dass die WPF-Befehlsinfrastruktur jedes Mal, wenn sie die integrierten Befehle befragt, alle DelegateCommand-Objekte fragt, ob sie ausgeführt werden können.

Zu Beginn des Artikels wurde gezeigt, wie vordefinierte Commands in XAML genutzt werden können. Im nächsten Artikel schreibe ich über „Binding“ in Bezug auf Commands und in Bezug auf Daten.

Verfasst von: bletra | 9. Mai 2011

WPF: Von Spaghetticode zu MVVM – Teil 4 von 9

In dieser Artikelserie möchte ich eine einfache Applikation, in eine testbare Applikation nach dem MVVM-Pattern umwandeln:

  1. Beispielapplikation mit Spaghetticode
  2. Refactoring 1: Model
  3. MVC, MVP, Presentation Model
  4. MVVM
  5. ICommand
  6. Binding
  7. Refactoring 2: ViewModel
  8. Refactoring 3: View
  9. Zusammenfassung

Model View ViewModel (MVVM)

Das MVVM-Pattern reiht sich in die Architekturmuster MVC, MVP und Presentation Model ein, wie ich sie im letzten Artikel eingeführt habe. MVVM ist ein Pattern, das sich bei WPF- und Silverlight-Applikationen zunehmend verbreitet. WPF möchte die Zusammenarbeit zwischen Designern und Entwicklern verbessern. Beide sollen mit der gleichen Codebasis arbeiten. Die deklarative Beschreibungssprache der Oberfläche Extensible Application Markup Language  (XAML) und Expression Blend  bieten die technische Voraussetzungen hierfür. XAML und der zugehörige C#-Code sind zwei verschiedene Sprachen für dasselbe Objektmodel. Jedes drag&drop, jedes XAML-Tag und jedes XAML-Attribut kann 1:1 auch in C# implementiert werden. Der XAML-Code kann Basis einer WPF- oder Silverlight-Applikation sein.

Das MVVM-Pattern entspricht prinzipiell dem Presentation Model-Pattern. Es verdient einen eigenen Namen, da es speziell die Möglichkeiten der .Net-Plattform nutzt, Presentation Model ist also das allgemeine Pattern und MVVM eine Spezialisierung für WPF und Silverlight.

Das ViewModel kapselt jegliches Verhalten (Befehle, Teil 5 dieser Serie) und Zustände der View. Ob ein Feld sichtbar ist oder nicht, der Cursor ein Wait-Cursor anzeigen soll, den Inhalt eines Textfeldes oder einer Listbox, dies alles sind Properties des ViewModels. Daher passt der Name ViewModel tatsächlich besser als Presenter, denn das ViewModel verwaltet die Daten in Bezug auf die View. Änderungen der View werden dem ViewModel über einen entsprechenden Mechanismus (Binding, Teil 6 dieser Serie) mitgeteilt, analog werden Änderungen des ViewModels der View mitgeteilt. Die folgende Abbildung stammt aus einem Artikel von Nikhil Kothari und veranschaulicht das MVVM-Pattern.

In den nächsten beiden Artikeln möchte ich die Basistechnologien Binding und Commands einführen, die MVVM massiv nutzt.

Verfasst von: bletra | 8. Mai 2011

WPF: Von Spaghetticode zu MVVM – Teil 3 von 9

In dieser Artikelserie möchte ich eine einfache Applikation, in eine testbare Applikation nach dem MVVM-Pattern umwandeln:

  1. Beispielapplikation mit Spaghetticode
  2. Refactoring 1: Model
  3. MVC, MVP, Presentation Model
  4. MVVM
  5. ICommand
  6. Binding
  7. Refactoring 2: ViewModel
  8. Refactoring 3: View
  9. Zusammenfassung

MVC, MVP, Presentation Model

Unser Ziel ist es, eine wartbare WPF-Applikation mit wieder verwendbaren Modulen zu erhalten. Hierfür steht das MVVM-Pattern.
MVVM ist wie MVC, MVP und Presentation Model ein Separations-Pattern, also ein Architekturpattern. Architekturpattern erleichtern neuen Entwicklern im Team das Eindenken in eine neue Applikation, sie finden sich leichter zurecht, wenn sie etwas Bekanntest wieder entdecken. MVVM baut auf den Erfahrungen mit MVC, MVP und Presentation Model auf. Daher führe ich diese hier kurz ein.

Model View Controller (MVC)

Das MVC-Muster ist ein zunehmend verbreitetes Architekturmuster. Bereits 1979 hat Trygve Reenskaug dieses Muster in Bezug auf GUI-basierte Smalltalk-Applikationen beschrieben (siehe MODELS – VIEWS – CONTROLLERS). Die Gang of Four (Erich Gamma, Richard Helm, Ralph Johnson und John Vlissides, abgekürzt mit GoF) veröffentlichte 1994 ein Buch zu Entwurfsmustern (siehe Design Patterns: Elements of Reusable Object-Oriented Software). Dieses Buch entwickelte sich schnell zu einem Standardwerk und brachte Entwurfsmuster in die Entwicklergemeinde. Die GoF-Entwurfsmuster haben sich zum Grundvokabular für objektorientiertes Design entwickelt. Beispielsweise wird dort das Observer-Pattern diskutiert, das aus GUI-basierten Applikationen nicht mehr wegzudenken ist.

Es folgte 1996 unter dem Namen POSA 1 (Pattern-Oriented Software Architecture) von F. Buschmann, R. Meunier, H. Rohnert, P. Sommerlad und M. Stal ein Katalog von Architekturmustern (siehe Pattern-Oriented Software Architecture Volume 1: A System of Patterns). Hier wird auch das MVC-Muster dargestellt:

“The Model-View-Controller architectural pattern (MVC) divides an interactive application into three components. The model contains the core functionality and data. Views display information to the user. Controllers handle user input. Views and controllers together comprise the user interface. A changepropagation mechanism ensures consistency between the user interface and the model”

Ziel des MVC-Musters ist die Trennung der Kernfunktionalität einer Applikation von der Interaktion mit der Benutzeroberfläche. Das MVC-Muster lässt sich als zusammengesetztes Muster, bestehend aus  Observer-, Strategy und Composite-Muster beschreiben:

  • Das Model (Observable oder Subjekt) implementiert das Observer-Muster. Interessierte Objekte, wie die View (Observer), können sich registrieren und werden über Zustandsänderungen informiert.
  • Die View nutzt das Composite-Muster. Sie setzt sich aus verschiedenen ineinander geschachtelten Komponenten zusammen (compose: zusammensetzen). Die View stellt die Daten des Models für die Benutzerin dar und bietet Interaktionsmöglichkeiten. Als Subscriber des Models wird sie über Zustandsänderungen des Models informiert. Die View ist für die visuelle Darstellung, die Präsentation zuständig.
  • Der Controller definiert das Verhalten (Strategie) der Applikation. Er ist für die Bearbeitung der Benutzerinteraktionen mit dem Model zuständig. Ein Button-Click wird beispielsweise durch den Controller in einen entsprechenden Methodenaufruf zum Schreiben von geänderten Daten im Model übersetzt.

Model View Presenter (MVP)

Beim MVC-Pattern referenziert die View das Model, um die veränderten Daten lesen zu können. Nur die Disziplin der Entwickler führt dazu, keine schreibende Methoden des Models aus der View aufzurufen und dies dem Controller zu überlassen. Mit MVP wird diese Verbindung zwischen Model und View gekappt. Dies diskutiert der Artikel MVC vs. MVP vs. MVVM aus dem die folgende Abbildung stammt:

Hinzu kommt der Einsatz von Schnittstellen für Model und View, so dass diese austauschbar werden und mit Mocking  leicht unabhängig zu testen sind.

Presentation Model (PM)

Martin Fowler beschreibt in „Presentation Model“ ein Architektur-Pattern, das Präsentationslogik und –Verhalten aus der View in ein Präsentations-Model auslagert.

„Presentation Model is not a GUI friendly facade to a specific domain object. Instead it is easier to consider Presentation Model as an abstract of the view that is not dependent on a specific GUI framework.”

Vergleich

Im JavaMagazin (10, 2006: Teile und herrsche) beschreibt der Autor sehr gut die Unterschiede und Gemeinsamkeiten zwischen MVC, Model-View-Presenter (MVP) und Presentation Model:

„Die View von MVC verarbeitet alle User Events, z.B. eine Tastatureingabe oder einen Mausklick. Bei MVP leitet die View diese Events an den Presenter weiter, und dieser ist für die Abarbeitung der Events verantwortlich. Anders als beim MVC Pattern besteht zwischen View und Model keine direkte Verbindung. Das Pattern Presentation Model unterscheidet sich vom MVP Pattern in zwei Punkten:

  • Das Presentation Model hält die Zustände der GUI-Komponenten der View. Ein Zustand kann z.B. sein, ob ein Textfeld editierbar ist oder nicht.
  • Während im MVP Pattern der Presenter die View referenziert, beobachtet beim PM Pattern die View Änderungen im Presentation Model.“

Auch Martin Fowler bietet in seinem Artikel GUI Architectures eine gute Übersicht über die genannten Muster.

Nach diesen Vorarbeiten kann ich im nächsten Artikel das MVVM-Pattern vorstellen.

Verfasst von: bletra | 7. Mai 2011

WPF: Von Spaghetticode zu MVVM – Teil 2 von 9

In dieser Artikelserie möchte ich eine einfache Applikation, in eine testbare Applikation nach dem MVVM-Pattern umwandeln:

  1. Beispielapplikation mit Spaghetticode
  2. Refactoring 1: Model
  3. MVC, MVP, Presentation Model
  4. MVVM
  5. ICommand
  6. Binding
  7. Refactoring 2: ViewModel
  8. Refactoring 3: View
  9. Zusammenfassung

Im ersten Artikel dieser Serie habe ich die zu verbessernde Applikation vorgestellt und erläutert, warum ein Refactoring notwendig ist. In diesem Artikel geht es um die Extraktion der Kernfunktionalität, die wir in einer Klasse FeedModel implementieren.

Refactoring 1: Model

Sie können hierzu mit einem neuen Projekt starten oder das vorhandene Projekt umgestalten (refactor). Erzeugen Sie eine neue Klasse (FeedModel) und verschieben Sie dorthin die Methoden LoadFeeds, die Kerninhalte der Methoden btnSave_Click und btnAddFeed_Click sowie den privaten Member _feeds. Im Konstruktor der Klasse rufen Sie die Methode LoadFeeds auf. Dabei verbessern Sie auch das Coding der Methoden und erhalten nun:

//Refactoring 1: Extraktion der Kernfunktionalität
public class FeedModel
{
    private Dictionary _feeds;
    private const string REPOSITORY_FILENAME = "feeds.dat";
    public FeedModel()
    {
        LoadFeeds();
    }
    public void SaveFeeds()
    {
        using (FileStream writeStream = new FileStream(REPOSITORY_FILENAME, FileMode.Create))
        {
            BinaryFormatter serializer = new BinaryFormatter();
            serializer.Serialize(writeStream, _feeds);
            writeStream.Close();
        }
    }
    public void AddFeed(string address)
    {
        Uri feedUri;
        Uri.TryCreate(address, UriKind.Absolute, out feedUri);
        if (feedUri == null)
            throw new UriFormatException("Error: The given address is no proper URI.");
        WebClient request = new WebClient();
        string xml = request.DownloadString(feedUri);
        if (xml.Length == 0)
            throw new XmlException("Error: The given address does not belong to a proper feed.");
        StringReader stringReader = new StringReader(xml);
        XmlReader reader = XmlReader.Create(stringReader);
        SyndicationFeed feed = SyndicationFeed.Load(reader);
        if (feed == null)
            throw new XmlException("Error: The given address does not belong to a proper feed.");
        if (_feeds.ContainsKey(feed.Title.Text))
            throw new DuplicateNameException("Error: The address is already listed.");
        _feeds.Add(feed.Title.Text, address);
    }

    public Dictionary Feeds
    {
        get { return _feeds; }
    }

    private void LoadFeeds()
    {
        if (!File.Exists(REPOSITORY_FILENAME))
        {
            _feeds = new Dictionary();
            return;
        }
        using (FileStream readStream = new FileStream(REPOSITORY_FILENAME, FileMode.Open))
        {
            BinaryFormatter serializer = new BinaryFormatter();
            _feeds = serializer.Deserialize(readStream) as Dictionary;
            readStream.Close();
        }
    }
}

Sie können nun nach InitializeComponent in MainWindow eine Instanz Ihres Models erzeugen, als privaten Member (_model) verfügbar machen und die Listbox mit lbFeeds.ItemsSource = _model.Feeds; an das Model binden. Bei btnAddFeed_Click etc. rufen Sie nun die entsprechenden Methoden des Models auf, z.B.:

private void btnAddFeed_Click(object sender, RoutedEventArgs e)
{
    string msg = "";
    try
    {
        _model.AddFeed(txtFeedAddress.Text);
        msg = "Added feed successfully.";
        txtFeedAddress.Text = string.Empty;
        lbFeeds.Items.Refresh();
    }
    catch(Exception ex)
    {
        msg = ex.Message;
    }
    MessageBox.Show(msg);
    this.Cursor = Cursors.Arrow;
}

Anstelle der MessageBox können Sie natürlich auch eine StatusBar verwenden und die Nachricht dort anzeigen.

Dieses Model können Sie auch in einer Asp.Net-Applikation analog verwenden und automatische Tests für die Methoden des Models schreiben.

WPF bieten noch weitere Möglichkeiten, die Applikation zu überarbeiten. Wir können auch die Logik der View kapseln. Beispielsweise soll der Add-Button disabled sein, wenn das Feld der Adresse leer ist. Die Modularisierung dieser View-Logik ermöglicht das MVVM-Pattern, das ich im übernächsten Artikel vorstellen möchte. MVVM basiert auf bekannte Architekturmuster auf, diese führe ich im nächsten Artikel ein.

« Newer Posts - Older Posts »

Kategorien