Dojos für Entwickler 2. Stefan Lieser

Чтение книги онлайн.

Читать онлайн книгу Dojos für Entwickler 2 - Stefan Lieser страница 4

Автор:
Серия:
Издательство:
Dojos für Entwickler 2 - Stefan Lieser

Скачать книгу

habe ich das durch exploratives Testen: Ich habe mir einen kleinen Testrahmen erstellt, in dem das UserControl verwendet wird. Darin habe ich dann mit verschiedenen Texten experimentiert und herausgefunden, dass nicht alle Kombinationen von fett und kursiv korrekt funktionierten. Als ich das erkannt hatte, habe ich die Integrationstests für die Platine ergänzt, um damit das Problem reproduzieren zu können. Erst als ich einen entsprechenden fehlschlagenden Test hatte, habe ich die Implementation korrigiert.

      UserControl

      Im letzten Schritt müssen die TextElement-Objekte noch in einem Control visualisiert werden. Ich habe dazu ein WPF-UserControl-Projekt erstellt. Das UserControl nimmt TextBlock-Controls in einem WrapPanel auf, die entsprechend den Angaben der jeweiligen TextElement-Objekte formatiert werden. Wenn die Eigenschaft Fett gesetzt ist, muss im TextBlock-Control die Font-Weight-Eigenschaft auf FontWeights.Bold gesetzt werden. Kursive Schrift wird erreicht, indem man die FontStyle-Eigenschaft auf den Wert FontStyles.Italic setzt.

      Um nun das WrapPanel-Control mit entsprechenden TextBlock-Controls zu befüllen, will ich natürlich wieder eine Funktionseinheit im Flow ergänzen. Dabei sollen die Abhängigkeiten zu den WPF-Assemblies jedoch nicht auf die bestehenden Projekte durchschlagen. Daher habe ich die Funktionseinheit, die aus TextElement-Objekten WPF-TextBlock-Objekte macht, im UserControl-Projekt untergebracht. Dort ist die Abhängigkeit zu den WPF-Assemblies ohnehin unvermeidbar. Listing 6 zeigt die Tests zur Funktionseinheit TextBlöcke_erzeugen.

      Listing 6

      Textblöcke testen.

      [TestFixture, RequiresSTA] public class TextBlöcke_erzeugen_Tests { private TextBlöcke_erzeugen sut; private IEnumerable<TextBlock> result; [SetUp] public void Setup() { sut = new TextBlöcke_erzeugen(); sut.Result += x => result = x; } [Test] public void Texte _werden_übernommen() { sut.Process(new[] { new TextElement {Text = "x"}, new TextElement {Text = "y"}, new TextElement {Text = "z"}, }); Assert.That(result.Select(x => x.Text), Is.EqualTo(new[] {"x", "y", "z"})); } [Test] public void Fett_Eigenschaft _wird_übernommen() { sut.Process(new[] { new TextElement {Fett = true}, new TextElement {Fett = false}, new TextElement {Fett = true}, }); Assert.That(result.Select( x => x.FontWeight), Is.EqualTo(new[] { FontWeights.Bold, FontWeights.Normal, FontWeights.Bold })); } [...] }

      Die Funktionseinheit muss für jedes TextElement-Objekt ein TextBlock-Objekt erzeugen. Da die WPF-Controls nur im sogenannten Single Thread Apartment (STA) laufen, ist die Testklasse mit dem Attribut RequiresSTA versehen. Die Tests überprüfen, ob der Text korrekt übernommen wird. Ferner wird überprüft, ob Fett- und Kursivschrift korrekt in FontWeight und FontStyle übernommen werden. Dabei lasse ich es für diese Funktionseinheit bewenden. Visuelle Controls erfordern nämlich ohnehin immer eine visuelle Prüfung. Am Ende interessiert den Anwender natürlich nicht, ob irgendwelche Texteigenschaften korrekt gesetzt sind, sondern der Text muss fett bzw. kursiv zu sehen sein, wie auch immer das intern bewerkstelligt wird. Folglich habe ich im WPF-Testprojekt eine Instanz des UserControls in der MainForm erzeugt und mit einem Beispieltext gefüllt. So kann ich visuell prüfen, ob die Formatierung korrekt ist. Abbildung 2 zeigt das Ergebnis. Listing 7 zeigt die Implementation der Funktionseinheit.

figure1b

      [Abb. 2]

       Visuelle Erscheinung des Controls.

      Listing 7

      Textblöcke erzeugen.

      public class TextBlöcke_erzeugen { public void Process( IEnumerable<TextElement> textElements) { Result(Alle_TextBlöcke _erzeugen(textElements)); } private IEnumerable<TextBlock> Alle_TextBlöcke_erzeugen( IEnumerable<TextElement> textElements) { foreach (var textElement in textElements) { yield return new TextBlock { Text = textElement.Text, FontStyle = textElement.Kursiv ? FontStyles.Italic : FontStyles.Normal, FontWeight = textElement.Fett ? FontWeights.Bold : FontWeights.Normal }; } } public event Action<IEnumerable< TextBlock>> Result; }

      Einzige Besonderheit in der Implementation ist die Methode Alle_TextBlöcke_ erzeugen. Die habe ich lediglich angelegt, um das yield return-Konstrukt verwenden zu können. Das Konstrukt steht nur in Methoden zur Verfügung, die einen Rückgabewert vom Typ IEnumerable<T> haben. Das ist bei der Methode Process nicht der Fall. Folglich habe ich die Schleife für die Erzeugung der TextBlock-Objekte in eine private Methode verschoben.

      Im UserControl muss nun noch der Flow zusammengebaut werden: eine Instanz der Platine Zerlege_MarkDown_Text muss mit dem Baustein TextBlöcke_erzeugen verbunden werden, siehe Listing 8.

      Listing 8

      Das fertige Steuerelement.

      public partial class MarkDownLabel : UserControl { private string markDownText; private readonly Action<string> parse_Markdown; public MarkDownLabel() { InitializeComponent(); var zerlege_MarkDown_Text = new Zerlege_MarkDown_Text(); var textBlöcke_erzeugen = new TextBlöcke_erzeugen(); zerlege_MarkDown_Text.Result += textBlöcke_erzeugen.Process; textBlöcke_erzeugen.Result += textBlocks => { wrapPanel.Children.Clear(); foreach (var textBlock in textBlocks) { wrapPanel.Children.Add(textBlock); } }; parse_Markdown = new Action<string>( zerlege_MarkDown_Text.Process); } public string MarkDownText { get { return markDownText; } set { markDownText = value; parse_Markdown(markDownText); } } }

      Immer wenn der Markdown-Text des UserControls geändert wird, muss der neue Text in den Flow gegeben werden. Am Ende kommen dann TextBlock-Controls heraus, die als Child-Controls in das WrapPanel eingefügt werden. Natürlich muss der bestehende Inhalt des WrapPanel-Controls zuvor entfernt werden. Die beiden Instanzen des Flows werden erzeugt und miteinander verbunden. Der Output von zerlege_ Mark-Down_Text wird zum Input von textBlöcke_ erzeugen, indem die Process-Methode mit dem Result-Event verbunden wird. Der Output von textBlöcke_erzeugen wird mittels Lambda-Expression ins WrapPanel-Control übernommen, nachdem dessen Inhalt mit Children.Clear entfernt wurde. Zuletzt wird eine Action parse_ Markdown als Feld der Klasse angelegt. Diese Action wird im Setter der MarkdownText-Eigenschaft aufgerufen, um den Text zu parsen und in TextBlock-Objekte zu übersetzen.

      Fazit

      Die vorgestellte Implementation ging leicht von der Hand. Da der Flow nicht groß ist, habe ich die Visualisierungsmöglichkeiten des ebclang-Toolings [3] nicht vermisst. Allerdings war ich bislang der Einzige, der in den Code geschaut hat. Vielleicht mögen Sie mir ja einen Leserbrief schreiben zur Frage, ob die Implementation durch eine ebc.xml-Datei zur Definition des Flows besser geworden wäre? [ml]

      [1] http://de.wikipedia.org/wiki/Markdown [2] http://daringfireball.net/projects/markdown/ [3] http://ebclang.codeplex.com

      Aufgabe 2

      Schlagworte für Digitalfotos

      Ordnung im Fotokarton

      Früher habe ich meine Fotos in Tüten gesteckt und in Schuhkartons gesammelt. Heute im Digitalzeitalter stehen mit Tags bessere Ordnungssysteme

Скачать книгу